diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js index f18b88b..00642b1 100644 --- a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -12,7 +12,7 @@ * @class * @namespace PIXI */ -function WebGLFastSpriteBatch(gl) { +function WebGLFastSpriteBatch(renderer) { /** * @member {number} */ @@ -93,11 +93,6 @@ /** * @member {object} */ - this.renderSession = null; - - /** - * @member {object} - */ this.shader = null; /** @@ -105,7 +100,9 @@ */ this.matrix = null; - this.setContext(gl); + this.renderer = renderer; + + this.setupContext(); } WebGLFastSpriteBatch.prototype.constructor = WebGLFastSpriteBatch; @@ -116,8 +113,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -WebGLFastSpriteBatch.prototype.setContext = function (gl) { - this.gl = gl; +WebGLFastSpriteBatch.prototype.setupContext = function () { + var gl = this.renderer.gl; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -135,11 +132,11 @@ /** * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {object} + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer */ -WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderSession) { - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; +WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderer) { + this.renderer = renderer; + this.shader = renderer.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); @@ -169,9 +166,9 @@ this.currentBaseTexture = sprite.texture.baseTexture; // check blend mode - if (sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { + if (sprite.blendMode !== this.renderer.blendModeManager.currentBlendMode) { this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + this.renderer.blendModeManager.setBlendMode(sprite.blendMode); } for (var i=0,j= children.length; i 1) { gl.viewport(0, 0, filterArea.width, filterArea.height); @@ -177,7 +183,7 @@ var inputTexture = texture; var outputTexture = this.texturePool.pop(); if (!outputTexture) { - outputTexture = new FilterTexture(this.gl, this.width, this.height); + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); } outputTexture.resize(this.width, this.height); @@ -297,7 +303,7 @@ this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); + // this.renderer.shaderManager.setShader(this.defaultShader); // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); @@ -317,7 +323,8 @@ */ WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) { // use program - var gl = this.gl; + var gl = this.renderer.gl; + var shader = filter.shaders[gl.id]; if (!shader) { @@ -331,7 +338,7 @@ } // set the shader - this.renderSession.shaderManager.setShader(shader); + this.renderer.shaderManager.setShader(shader); // gl.useProgram(shader.program); @@ -361,7 +368,7 @@ // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - this.renderSession.drawCount++; + this.renderer.drawCount++; }; /** @@ -369,7 +376,7 @@ * */ WebGLFilterManager.prototype.initShaderBuffers = function () { - var gl = this.gl; + var gl = this.renderer.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); @@ -415,7 +422,7 @@ * */ WebGLFilterManager.prototype.destroy = function () { - var gl = this.gl; + var gl = this.renderer.gl; this.filterStack = null; @@ -434,4 +441,6 @@ gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; }; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js index f18b88b..00642b1 100644 --- a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -12,7 +12,7 @@ * @class * @namespace PIXI */ -function WebGLFastSpriteBatch(gl) { +function WebGLFastSpriteBatch(renderer) { /** * @member {number} */ @@ -93,11 +93,6 @@ /** * @member {object} */ - this.renderSession = null; - - /** - * @member {object} - */ this.shader = null; /** @@ -105,7 +100,9 @@ */ this.matrix = null; - this.setContext(gl); + this.renderer = renderer; + + this.setupContext(); } WebGLFastSpriteBatch.prototype.constructor = WebGLFastSpriteBatch; @@ -116,8 +113,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -WebGLFastSpriteBatch.prototype.setContext = function (gl) { - this.gl = gl; +WebGLFastSpriteBatch.prototype.setupContext = function () { + var gl = this.renderer.gl; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -135,11 +132,11 @@ /** * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {object} + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer */ -WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderSession) { - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; +WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderer) { + this.renderer = renderer; + this.shader = renderer.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); @@ -169,9 +166,9 @@ this.currentBaseTexture = sprite.texture.baseTexture; // check blend mode - if (sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { + if (sprite.blendMode !== this.renderer.blendModeManager.currentBlendMode) { this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + this.renderer.blendModeManager.setBlendMode(sprite.blendMode); } for (var i=0,j= children.length; i 1) { gl.viewport(0, 0, filterArea.width, filterArea.height); @@ -177,7 +183,7 @@ var inputTexture = texture; var outputTexture = this.texturePool.pop(); if (!outputTexture) { - outputTexture = new FilterTexture(this.gl, this.width, this.height); + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); } outputTexture.resize(this.width, this.height); @@ -297,7 +303,7 @@ this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); + // this.renderer.shaderManager.setShader(this.defaultShader); // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); @@ -317,7 +323,8 @@ */ WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) { // use program - var gl = this.gl; + var gl = this.renderer.gl; + var shader = filter.shaders[gl.id]; if (!shader) { @@ -331,7 +338,7 @@ } // set the shader - this.renderSession.shaderManager.setShader(shader); + this.renderer.shaderManager.setShader(shader); // gl.useProgram(shader.program); @@ -361,7 +368,7 @@ // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - this.renderSession.drawCount++; + this.renderer.drawCount++; }; /** @@ -369,7 +376,7 @@ * */ WebGLFilterManager.prototype.initShaderBuffers = function () { - var gl = this.gl; + var gl = this.renderer.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); @@ -415,7 +422,7 @@ * */ WebGLFilterManager.prototype.destroy = function () { - var gl = this.gl; + var gl = this.renderer.gl; this.filterStack = null; @@ -434,4 +441,6 @@ gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLGraphics.js b/src/core/renderers/webgl/utils/WebGLGraphics.js index 86efe8f..09f540f 100644 --- a/src/core/renderers/webgl/utils/WebGLGraphics.js +++ b/src/core/renderers/webgl/utils/WebGLGraphics.js @@ -17,13 +17,14 @@ * @static * @private * @param graphics {Graphics} - * @param renderSession {object} + * @param renderer {WebGLRenderer} */ -WebGLGraphics.renderGraphics = function (graphics, renderSession) {//projection, offset) { - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader, +WebGLGraphics.renderGraphics = function (graphics, renderer) {//projection, offset) { + var gl = renderer.gl; + + var projection = renderer.projection, + offset = renderer.offset, + shader = renderer.shaderManager.primitiveShader, webGLData; if (graphics.dirty) { @@ -38,19 +39,19 @@ if (webGL.data[i].mode === 1) { webGLData = webGL.data[i]; - renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); + renderer.stencilManager.pushStencil(graphics, webGLData, renderer); // render quad.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + renderer.stencilManager.popStencil(graphics, webGLData, renderer); } else { webGLData = webGL.data[i]; - renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderer.shaderManager.primitiveShader; gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, 1); diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js index f18b88b..00642b1 100644 --- a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -12,7 +12,7 @@ * @class * @namespace PIXI */ -function WebGLFastSpriteBatch(gl) { +function WebGLFastSpriteBatch(renderer) { /** * @member {number} */ @@ -93,11 +93,6 @@ /** * @member {object} */ - this.renderSession = null; - - /** - * @member {object} - */ this.shader = null; /** @@ -105,7 +100,9 @@ */ this.matrix = null; - this.setContext(gl); + this.renderer = renderer; + + this.setupContext(); } WebGLFastSpriteBatch.prototype.constructor = WebGLFastSpriteBatch; @@ -116,8 +113,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -WebGLFastSpriteBatch.prototype.setContext = function (gl) { - this.gl = gl; +WebGLFastSpriteBatch.prototype.setupContext = function () { + var gl = this.renderer.gl; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -135,11 +132,11 @@ /** * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {object} + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer */ -WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderSession) { - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; +WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderer) { + this.renderer = renderer; + this.shader = renderer.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); @@ -169,9 +166,9 @@ this.currentBaseTexture = sprite.texture.baseTexture; // check blend mode - if (sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { + if (sprite.blendMode !== this.renderer.blendModeManager.currentBlendMode) { this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + this.renderer.blendModeManager.setBlendMode(sprite.blendMode); } for (var i=0,j= children.length; i 1) { gl.viewport(0, 0, filterArea.width, filterArea.height); @@ -177,7 +183,7 @@ var inputTexture = texture; var outputTexture = this.texturePool.pop(); if (!outputTexture) { - outputTexture = new FilterTexture(this.gl, this.width, this.height); + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); } outputTexture.resize(this.width, this.height); @@ -297,7 +303,7 @@ this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); + // this.renderer.shaderManager.setShader(this.defaultShader); // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); @@ -317,7 +323,8 @@ */ WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) { // use program - var gl = this.gl; + var gl = this.renderer.gl; + var shader = filter.shaders[gl.id]; if (!shader) { @@ -331,7 +338,7 @@ } // set the shader - this.renderSession.shaderManager.setShader(shader); + this.renderer.shaderManager.setShader(shader); // gl.useProgram(shader.program); @@ -361,7 +368,7 @@ // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - this.renderSession.drawCount++; + this.renderer.drawCount++; }; /** @@ -369,7 +376,7 @@ * */ WebGLFilterManager.prototype.initShaderBuffers = function () { - var gl = this.gl; + var gl = this.renderer.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); @@ -415,7 +422,7 @@ * */ WebGLFilterManager.prototype.destroy = function () { - var gl = this.gl; + var gl = this.renderer.gl; this.filterStack = null; @@ -434,4 +441,6 @@ gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLGraphics.js b/src/core/renderers/webgl/utils/WebGLGraphics.js index 86efe8f..09f540f 100644 --- a/src/core/renderers/webgl/utils/WebGLGraphics.js +++ b/src/core/renderers/webgl/utils/WebGLGraphics.js @@ -17,13 +17,14 @@ * @static * @private * @param graphics {Graphics} - * @param renderSession {object} + * @param renderer {WebGLRenderer} */ -WebGLGraphics.renderGraphics = function (graphics, renderSession) {//projection, offset) { - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader, +WebGLGraphics.renderGraphics = function (graphics, renderer) {//projection, offset) { + var gl = renderer.gl; + + var projection = renderer.projection, + offset = renderer.offset, + shader = renderer.shaderManager.primitiveShader, webGLData; if (graphics.dirty) { @@ -38,19 +39,19 @@ if (webGL.data[i].mode === 1) { webGLData = webGL.data[i]; - renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); + renderer.stencilManager.pushStencil(graphics, webGLData, renderer); // render quad.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + renderer.stencilManager.popStencil(graphics, webGLData, renderer); } else { webGLData = webGL.data[i]; - renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderer.shaderManager.primitiveShader; gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, 1); diff --git a/src/core/renderers/webgl/utils/WebGLManager.js b/src/core/renderers/webgl/utils/WebGLManager.js new file mode 100644 index 0000000..9d47b5c --- /dev/null +++ b/src/core/renderers/webgl/utils/WebGLManager.js @@ -0,0 +1,20 @@ +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLManager(renderer) { + /** + * The renderer this manager works for. + * + * @member {WebGLRenderer} + */ + this.renderer = renderer; +} + +WebGLManager.prototype.constructor = WebGLManager; +module.exports = WebGLManager; + +WebGLManager.prototype.destroy = function () { + this.renderer = null; +}; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js index f18b88b..00642b1 100644 --- a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -12,7 +12,7 @@ * @class * @namespace PIXI */ -function WebGLFastSpriteBatch(gl) { +function WebGLFastSpriteBatch(renderer) { /** * @member {number} */ @@ -93,11 +93,6 @@ /** * @member {object} */ - this.renderSession = null; - - /** - * @member {object} - */ this.shader = null; /** @@ -105,7 +100,9 @@ */ this.matrix = null; - this.setContext(gl); + this.renderer = renderer; + + this.setupContext(); } WebGLFastSpriteBatch.prototype.constructor = WebGLFastSpriteBatch; @@ -116,8 +113,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -WebGLFastSpriteBatch.prototype.setContext = function (gl) { - this.gl = gl; +WebGLFastSpriteBatch.prototype.setupContext = function () { + var gl = this.renderer.gl; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -135,11 +132,11 @@ /** * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {object} + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer */ -WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderSession) { - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; +WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderer) { + this.renderer = renderer; + this.shader = renderer.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); @@ -169,9 +166,9 @@ this.currentBaseTexture = sprite.texture.baseTexture; // check blend mode - if (sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { + if (sprite.blendMode !== this.renderer.blendModeManager.currentBlendMode) { this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + this.renderer.blendModeManager.setBlendMode(sprite.blendMode); } for (var i=0,j= children.length; i 1) { gl.viewport(0, 0, filterArea.width, filterArea.height); @@ -177,7 +183,7 @@ var inputTexture = texture; var outputTexture = this.texturePool.pop(); if (!outputTexture) { - outputTexture = new FilterTexture(this.gl, this.width, this.height); + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); } outputTexture.resize(this.width, this.height); @@ -297,7 +303,7 @@ this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); + // this.renderer.shaderManager.setShader(this.defaultShader); // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); @@ -317,7 +323,8 @@ */ WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) { // use program - var gl = this.gl; + var gl = this.renderer.gl; + var shader = filter.shaders[gl.id]; if (!shader) { @@ -331,7 +338,7 @@ } // set the shader - this.renderSession.shaderManager.setShader(shader); + this.renderer.shaderManager.setShader(shader); // gl.useProgram(shader.program); @@ -361,7 +368,7 @@ // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - this.renderSession.drawCount++; + this.renderer.drawCount++; }; /** @@ -369,7 +376,7 @@ * */ WebGLFilterManager.prototype.initShaderBuffers = function () { - var gl = this.gl; + var gl = this.renderer.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); @@ -415,7 +422,7 @@ * */ WebGLFilterManager.prototype.destroy = function () { - var gl = this.gl; + var gl = this.renderer.gl; this.filterStack = null; @@ -434,4 +441,6 @@ gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLGraphics.js b/src/core/renderers/webgl/utils/WebGLGraphics.js index 86efe8f..09f540f 100644 --- a/src/core/renderers/webgl/utils/WebGLGraphics.js +++ b/src/core/renderers/webgl/utils/WebGLGraphics.js @@ -17,13 +17,14 @@ * @static * @private * @param graphics {Graphics} - * @param renderSession {object} + * @param renderer {WebGLRenderer} */ -WebGLGraphics.renderGraphics = function (graphics, renderSession) {//projection, offset) { - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader, +WebGLGraphics.renderGraphics = function (graphics, renderer) {//projection, offset) { + var gl = renderer.gl; + + var projection = renderer.projection, + offset = renderer.offset, + shader = renderer.shaderManager.primitiveShader, webGLData; if (graphics.dirty) { @@ -38,19 +39,19 @@ if (webGL.data[i].mode === 1) { webGLData = webGL.data[i]; - renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); + renderer.stencilManager.pushStencil(graphics, webGLData, renderer); // render quad.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + renderer.stencilManager.popStencil(graphics, webGLData, renderer); } else { webGLData = webGL.data[i]; - renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderer.shaderManager.primitiveShader; gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, 1); diff --git a/src/core/renderers/webgl/utils/WebGLManager.js b/src/core/renderers/webgl/utils/WebGLManager.js new file mode 100644 index 0000000..9d47b5c --- /dev/null +++ b/src/core/renderers/webgl/utils/WebGLManager.js @@ -0,0 +1,20 @@ +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLManager(renderer) { + /** + * The renderer this manager works for. + * + * @member {WebGLRenderer} + */ + this.renderer = renderer; +} + +WebGLManager.prototype.constructor = WebGLManager; +module.exports = WebGLManager; + +WebGLManager.prototype.destroy = function () { + this.renderer = null; +}; diff --git a/src/core/renderers/webgl/utils/WebGLMaskManager.js b/src/core/renderers/webgl/utils/WebGLMaskManager.js index 002c54d..6d46d85 100644 --- a/src/core/renderers/webgl/utils/WebGLMaskManager.js +++ b/src/core/renderers/webgl/utils/WebGLMaskManager.js @@ -1,59 +1,41 @@ -var WebGLGraphics = require('./WebGLGraphics'); +var WebGLManager = require('./WebGLManager'), + WebGLGraphics = require('./WebGLGraphics'); /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLMaskManager() {} +function WebGLMaskManager(renderer) { + WebGLManager.call(this, renderer); +} +WebGLMaskManager.prototype = Object.create(WebGLManager.prototype); WebGLMaskManager.prototype.constructor = WebGLMaskManager; module.exports = WebGLMaskManager; /** - * Sets the drawing context to the one given in parameter. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLMaskManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Applies the Mask and adds it to the current filter stack. * - * @param maskData {Array} - * @param renderSession {object} + * @param maskData {any[]} */ -WebGLMaskManager.prototype.pushMask = function (maskData, renderSession) { - var gl = renderSession.gl; - +WebGLMaskManager.prototype.pushMask = function (maskData) { if (maskData.dirty) { - WebGLGraphics.updateGraphics(maskData, gl); + WebGLGraphics.updateGraphics(maskData, this.renderer.gl); } - if (!maskData._webGL[gl.id].data.length) { + if (!maskData._webGL[this.renderer.gl.id].data.length) { return; } - renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); + this.renderer.stencilManager.pushStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; /** * Removes the last filter from the filter stack and doesn't return it. * - * @param maskData {Array} - * @param renderSession {object} an object containing all the useful parameters + * @param maskData {any[]} */ -WebGLMaskManager.prototype.popMask = function (maskData, renderSession) { - var gl = this.gl; - renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); -}; - -/** - * Destroys the mask stack. - * - */ -WebGLMaskManager.prototype.destroy = function () { - this.gl = null; +WebGLMaskManager.prototype.popMask = function (maskData) { + this.renderer.stencilManager.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js index f18b88b..00642b1 100644 --- a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -12,7 +12,7 @@ * @class * @namespace PIXI */ -function WebGLFastSpriteBatch(gl) { +function WebGLFastSpriteBatch(renderer) { /** * @member {number} */ @@ -93,11 +93,6 @@ /** * @member {object} */ - this.renderSession = null; - - /** - * @member {object} - */ this.shader = null; /** @@ -105,7 +100,9 @@ */ this.matrix = null; - this.setContext(gl); + this.renderer = renderer; + + this.setupContext(); } WebGLFastSpriteBatch.prototype.constructor = WebGLFastSpriteBatch; @@ -116,8 +113,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -WebGLFastSpriteBatch.prototype.setContext = function (gl) { - this.gl = gl; +WebGLFastSpriteBatch.prototype.setupContext = function () { + var gl = this.renderer.gl; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -135,11 +132,11 @@ /** * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {object} + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer */ -WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderSession) { - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; +WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderer) { + this.renderer = renderer; + this.shader = renderer.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); @@ -169,9 +166,9 @@ this.currentBaseTexture = sprite.texture.baseTexture; // check blend mode - if (sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { + if (sprite.blendMode !== this.renderer.blendModeManager.currentBlendMode) { this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + this.renderer.blendModeManager.setBlendMode(sprite.blendMode); } for (var i=0,j= children.length; i 1) { gl.viewport(0, 0, filterArea.width, filterArea.height); @@ -177,7 +183,7 @@ var inputTexture = texture; var outputTexture = this.texturePool.pop(); if (!outputTexture) { - outputTexture = new FilterTexture(this.gl, this.width, this.height); + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); } outputTexture.resize(this.width, this.height); @@ -297,7 +303,7 @@ this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); + // this.renderer.shaderManager.setShader(this.defaultShader); // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); @@ -317,7 +323,8 @@ */ WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) { // use program - var gl = this.gl; + var gl = this.renderer.gl; + var shader = filter.shaders[gl.id]; if (!shader) { @@ -331,7 +338,7 @@ } // set the shader - this.renderSession.shaderManager.setShader(shader); + this.renderer.shaderManager.setShader(shader); // gl.useProgram(shader.program); @@ -361,7 +368,7 @@ // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - this.renderSession.drawCount++; + this.renderer.drawCount++; }; /** @@ -369,7 +376,7 @@ * */ WebGLFilterManager.prototype.initShaderBuffers = function () { - var gl = this.gl; + var gl = this.renderer.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); @@ -415,7 +422,7 @@ * */ WebGLFilterManager.prototype.destroy = function () { - var gl = this.gl; + var gl = this.renderer.gl; this.filterStack = null; @@ -434,4 +441,6 @@ gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLGraphics.js b/src/core/renderers/webgl/utils/WebGLGraphics.js index 86efe8f..09f540f 100644 --- a/src/core/renderers/webgl/utils/WebGLGraphics.js +++ b/src/core/renderers/webgl/utils/WebGLGraphics.js @@ -17,13 +17,14 @@ * @static * @private * @param graphics {Graphics} - * @param renderSession {object} + * @param renderer {WebGLRenderer} */ -WebGLGraphics.renderGraphics = function (graphics, renderSession) {//projection, offset) { - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader, +WebGLGraphics.renderGraphics = function (graphics, renderer) {//projection, offset) { + var gl = renderer.gl; + + var projection = renderer.projection, + offset = renderer.offset, + shader = renderer.shaderManager.primitiveShader, webGLData; if (graphics.dirty) { @@ -38,19 +39,19 @@ if (webGL.data[i].mode === 1) { webGLData = webGL.data[i]; - renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); + renderer.stencilManager.pushStencil(graphics, webGLData, renderer); // render quad.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + renderer.stencilManager.popStencil(graphics, webGLData, renderer); } else { webGLData = webGL.data[i]; - renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderer.shaderManager.primitiveShader; gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, 1); diff --git a/src/core/renderers/webgl/utils/WebGLManager.js b/src/core/renderers/webgl/utils/WebGLManager.js new file mode 100644 index 0000000..9d47b5c --- /dev/null +++ b/src/core/renderers/webgl/utils/WebGLManager.js @@ -0,0 +1,20 @@ +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLManager(renderer) { + /** + * The renderer this manager works for. + * + * @member {WebGLRenderer} + */ + this.renderer = renderer; +} + +WebGLManager.prototype.constructor = WebGLManager; +module.exports = WebGLManager; + +WebGLManager.prototype.destroy = function () { + this.renderer = null; +}; diff --git a/src/core/renderers/webgl/utils/WebGLMaskManager.js b/src/core/renderers/webgl/utils/WebGLMaskManager.js index 002c54d..6d46d85 100644 --- a/src/core/renderers/webgl/utils/WebGLMaskManager.js +++ b/src/core/renderers/webgl/utils/WebGLMaskManager.js @@ -1,59 +1,41 @@ -var WebGLGraphics = require('./WebGLGraphics'); +var WebGLManager = require('./WebGLManager'), + WebGLGraphics = require('./WebGLGraphics'); /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLMaskManager() {} +function WebGLMaskManager(renderer) { + WebGLManager.call(this, renderer); +} +WebGLMaskManager.prototype = Object.create(WebGLManager.prototype); WebGLMaskManager.prototype.constructor = WebGLMaskManager; module.exports = WebGLMaskManager; /** - * Sets the drawing context to the one given in parameter. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLMaskManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Applies the Mask and adds it to the current filter stack. * - * @param maskData {Array} - * @param renderSession {object} + * @param maskData {any[]} */ -WebGLMaskManager.prototype.pushMask = function (maskData, renderSession) { - var gl = renderSession.gl; - +WebGLMaskManager.prototype.pushMask = function (maskData) { if (maskData.dirty) { - WebGLGraphics.updateGraphics(maskData, gl); + WebGLGraphics.updateGraphics(maskData, this.renderer.gl); } - if (!maskData._webGL[gl.id].data.length) { + if (!maskData._webGL[this.renderer.gl.id].data.length) { return; } - renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); + this.renderer.stencilManager.pushStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; /** * Removes the last filter from the filter stack and doesn't return it. * - * @param maskData {Array} - * @param renderSession {object} an object containing all the useful parameters + * @param maskData {any[]} */ -WebGLMaskManager.prototype.popMask = function (maskData, renderSession) { - var gl = this.gl; - renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); -}; - -/** - * Destroys the mask stack. - * - */ -WebGLMaskManager.prototype.destroy = function () { - this.gl = null; +WebGLMaskManager.prototype.popMask = function (maskData) { + this.renderer.stencilManager.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderManager.js b/src/core/renderers/webgl/utils/WebGLShaderManager.js index 7f2c443..d02d95d 100644 --- a/src/core/renderers/webgl/utils/WebGLShaderManager.js +++ b/src/core/renderers/webgl/utils/WebGLShaderManager.js @@ -1,4 +1,5 @@ -var PrimitiveShader = require('../shaders/PrimitiveShader'), +var WebGLManager = require('./WebGLManager'), + PrimitiveShader = require('../shaders/PrimitiveShader'), ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), PixiShader = require('../shaders/PixiShader'), PixiFastShader = require('../shaders/PixiFastShader'), @@ -7,21 +8,23 @@ /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLShaderManager() { +function WebGLShaderManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.maxAttibs = 10; /** - * @member {Array} + * @member {any[]} */ this.attribState = []; /** - * @member {Array} + * @member {any[]} */ this.tempAttribState = []; @@ -30,38 +33,62 @@ } /** - * @member {Array} + * @member {any[]} */ this.stack = []; -} -WebGLShaderManager.prototype.constructor = WebGLShaderManager; -module.exports = WebGLShaderManager; + /** + * @member {number} + * @private + */ + this._currentId = -1; -/** - * Initialises the context and the properties. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLShaderManager.prototype.setContext = function (gl) { - this.gl = gl; + /** + * @member {Shader} + * @private + */ + this.currentShader = null; - // the next one is used for rendering primitives - this.primitiveShader = new PrimitiveShader(gl); + // this shader is used for rendering primitives + this.primitiveShader = null; - // the next one is used for rendering triangle strips - this.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + // this shader is used for rendering triangle strips + this.complexPrimitiveShader = null; // this shader is used for the default sprite rendering - this.defaultShader = new PixiShader(gl); + this.defaultShader = null; // this shader is used for the fast sprite rendering - this.fastShader = new PixiFastShader(gl); + this.fastShader = null; // the next one is used for rendering triangle strips - this.stripShader = new StripShader(gl); - this.setShader(this.defaultShader); -}; + this.stripShader = null; + + // listen for context and update necessary shaders + var self = this; + this.renderer.on('context', function (gl) { + // this shader is used for rendering primitives + self.primitiveShader = new PrimitiveShader(gl); + + // this shader is used for rendering triangle strips + self.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + + // this shader is used for the default sprite rendering + self.defaultShader = new PixiShader(gl); + + // this shader is used for the fast sprite rendering + self.fastShader = new PixiFastShader(gl); + + // the next one is used for rendering triangle strips + self.stripShader = new StripShader(gl); + + self.setShader(self.defaultShader); + }); +} + +WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); +WebGLShaderManager.prototype.constructor = WebGLShaderManager; +module.exports = WebGLShaderManager; /** * Takes the attributes given in parameters. @@ -77,18 +104,17 @@ } // set the new attribs - for (i = 0; i < attribs.length; i++) { - var attribId = attribs[i]; - this.tempAttribState[attribId] = true; + for (var a in attribs) { + this.tempAttribState[attribs[a]] = true; } - var gl = this.gl; + var gl = this.renderer.gl; for (i = 0; i < this.attribState.length; i++) { if (this.attribState[i] !== this.tempAttribState[i]) { this.attribState[i] = this.tempAttribState[i]; - if (this.tempAttribState[i]) { + if (this.attribState[i]) { gl.enableVertexAttribArray(i); } else { @@ -104,15 +130,15 @@ * @param shader {Any} */ WebGLShaderManager.prototype.setShader = function (shader) { - if (this._currentId === shader._UID) { + if (this._currentId === shader.uuid) { return false; } - this._currentId = shader._UID; + this._currentId = shader.uuid; this.currentShader = shader; - this.gl.useProgram(shader.program); + this.renderer.gl.useProgram(shader.program); this.setAttribs(shader.attributes); return true; @@ -128,14 +154,19 @@ this.tempAttribState = null; this.primitiveShader.destroy(); + this.primitiveShader = null; this.complexPrimitiveShader.destroy(); + this.complexPrimitiveShader = null; this.defaultShader.destroy(); + this.defaultShader = null; this.fastShader.destroy(); + this.fastShader = null; this.stripShader.destroy(); + this.stripShader = null; - this.gl = null; + this.renderer = null; }; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js index f18b88b..00642b1 100644 --- a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -12,7 +12,7 @@ * @class * @namespace PIXI */ -function WebGLFastSpriteBatch(gl) { +function WebGLFastSpriteBatch(renderer) { /** * @member {number} */ @@ -93,11 +93,6 @@ /** * @member {object} */ - this.renderSession = null; - - /** - * @member {object} - */ this.shader = null; /** @@ -105,7 +100,9 @@ */ this.matrix = null; - this.setContext(gl); + this.renderer = renderer; + + this.setupContext(); } WebGLFastSpriteBatch.prototype.constructor = WebGLFastSpriteBatch; @@ -116,8 +113,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -WebGLFastSpriteBatch.prototype.setContext = function (gl) { - this.gl = gl; +WebGLFastSpriteBatch.prototype.setupContext = function () { + var gl = this.renderer.gl; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -135,11 +132,11 @@ /** * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {object} + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer */ -WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderSession) { - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; +WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderer) { + this.renderer = renderer; + this.shader = renderer.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); @@ -169,9 +166,9 @@ this.currentBaseTexture = sprite.texture.baseTexture; // check blend mode - if (sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { + if (sprite.blendMode !== this.renderer.blendModeManager.currentBlendMode) { this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + this.renderer.blendModeManager.setBlendMode(sprite.blendMode); } for (var i=0,j= children.length; i 1) { gl.viewport(0, 0, filterArea.width, filterArea.height); @@ -177,7 +183,7 @@ var inputTexture = texture; var outputTexture = this.texturePool.pop(); if (!outputTexture) { - outputTexture = new FilterTexture(this.gl, this.width, this.height); + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); } outputTexture.resize(this.width, this.height); @@ -297,7 +303,7 @@ this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); + // this.renderer.shaderManager.setShader(this.defaultShader); // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); @@ -317,7 +323,8 @@ */ WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) { // use program - var gl = this.gl; + var gl = this.renderer.gl; + var shader = filter.shaders[gl.id]; if (!shader) { @@ -331,7 +338,7 @@ } // set the shader - this.renderSession.shaderManager.setShader(shader); + this.renderer.shaderManager.setShader(shader); // gl.useProgram(shader.program); @@ -361,7 +368,7 @@ // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - this.renderSession.drawCount++; + this.renderer.drawCount++; }; /** @@ -369,7 +376,7 @@ * */ WebGLFilterManager.prototype.initShaderBuffers = function () { - var gl = this.gl; + var gl = this.renderer.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); @@ -415,7 +422,7 @@ * */ WebGLFilterManager.prototype.destroy = function () { - var gl = this.gl; + var gl = this.renderer.gl; this.filterStack = null; @@ -434,4 +441,6 @@ gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLGraphics.js b/src/core/renderers/webgl/utils/WebGLGraphics.js index 86efe8f..09f540f 100644 --- a/src/core/renderers/webgl/utils/WebGLGraphics.js +++ b/src/core/renderers/webgl/utils/WebGLGraphics.js @@ -17,13 +17,14 @@ * @static * @private * @param graphics {Graphics} - * @param renderSession {object} + * @param renderer {WebGLRenderer} */ -WebGLGraphics.renderGraphics = function (graphics, renderSession) {//projection, offset) { - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader, +WebGLGraphics.renderGraphics = function (graphics, renderer) {//projection, offset) { + var gl = renderer.gl; + + var projection = renderer.projection, + offset = renderer.offset, + shader = renderer.shaderManager.primitiveShader, webGLData; if (graphics.dirty) { @@ -38,19 +39,19 @@ if (webGL.data[i].mode === 1) { webGLData = webGL.data[i]; - renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); + renderer.stencilManager.pushStencil(graphics, webGLData, renderer); // render quad.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + renderer.stencilManager.popStencil(graphics, webGLData, renderer); } else { webGLData = webGL.data[i]; - renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderer.shaderManager.primitiveShader; gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, 1); diff --git a/src/core/renderers/webgl/utils/WebGLManager.js b/src/core/renderers/webgl/utils/WebGLManager.js new file mode 100644 index 0000000..9d47b5c --- /dev/null +++ b/src/core/renderers/webgl/utils/WebGLManager.js @@ -0,0 +1,20 @@ +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLManager(renderer) { + /** + * The renderer this manager works for. + * + * @member {WebGLRenderer} + */ + this.renderer = renderer; +} + +WebGLManager.prototype.constructor = WebGLManager; +module.exports = WebGLManager; + +WebGLManager.prototype.destroy = function () { + this.renderer = null; +}; diff --git a/src/core/renderers/webgl/utils/WebGLMaskManager.js b/src/core/renderers/webgl/utils/WebGLMaskManager.js index 002c54d..6d46d85 100644 --- a/src/core/renderers/webgl/utils/WebGLMaskManager.js +++ b/src/core/renderers/webgl/utils/WebGLMaskManager.js @@ -1,59 +1,41 @@ -var WebGLGraphics = require('./WebGLGraphics'); +var WebGLManager = require('./WebGLManager'), + WebGLGraphics = require('./WebGLGraphics'); /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLMaskManager() {} +function WebGLMaskManager(renderer) { + WebGLManager.call(this, renderer); +} +WebGLMaskManager.prototype = Object.create(WebGLManager.prototype); WebGLMaskManager.prototype.constructor = WebGLMaskManager; module.exports = WebGLMaskManager; /** - * Sets the drawing context to the one given in parameter. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLMaskManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Applies the Mask and adds it to the current filter stack. * - * @param maskData {Array} - * @param renderSession {object} + * @param maskData {any[]} */ -WebGLMaskManager.prototype.pushMask = function (maskData, renderSession) { - var gl = renderSession.gl; - +WebGLMaskManager.prototype.pushMask = function (maskData) { if (maskData.dirty) { - WebGLGraphics.updateGraphics(maskData, gl); + WebGLGraphics.updateGraphics(maskData, this.renderer.gl); } - if (!maskData._webGL[gl.id].data.length) { + if (!maskData._webGL[this.renderer.gl.id].data.length) { return; } - renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); + this.renderer.stencilManager.pushStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; /** * Removes the last filter from the filter stack and doesn't return it. * - * @param maskData {Array} - * @param renderSession {object} an object containing all the useful parameters + * @param maskData {any[]} */ -WebGLMaskManager.prototype.popMask = function (maskData, renderSession) { - var gl = this.gl; - renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); -}; - -/** - * Destroys the mask stack. - * - */ -WebGLMaskManager.prototype.destroy = function () { - this.gl = null; +WebGLMaskManager.prototype.popMask = function (maskData) { + this.renderer.stencilManager.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderManager.js b/src/core/renderers/webgl/utils/WebGLShaderManager.js index 7f2c443..d02d95d 100644 --- a/src/core/renderers/webgl/utils/WebGLShaderManager.js +++ b/src/core/renderers/webgl/utils/WebGLShaderManager.js @@ -1,4 +1,5 @@ -var PrimitiveShader = require('../shaders/PrimitiveShader'), +var WebGLManager = require('./WebGLManager'), + PrimitiveShader = require('../shaders/PrimitiveShader'), ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), PixiShader = require('../shaders/PixiShader'), PixiFastShader = require('../shaders/PixiFastShader'), @@ -7,21 +8,23 @@ /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLShaderManager() { +function WebGLShaderManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.maxAttibs = 10; /** - * @member {Array} + * @member {any[]} */ this.attribState = []; /** - * @member {Array} + * @member {any[]} */ this.tempAttribState = []; @@ -30,38 +33,62 @@ } /** - * @member {Array} + * @member {any[]} */ this.stack = []; -} -WebGLShaderManager.prototype.constructor = WebGLShaderManager; -module.exports = WebGLShaderManager; + /** + * @member {number} + * @private + */ + this._currentId = -1; -/** - * Initialises the context and the properties. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLShaderManager.prototype.setContext = function (gl) { - this.gl = gl; + /** + * @member {Shader} + * @private + */ + this.currentShader = null; - // the next one is used for rendering primitives - this.primitiveShader = new PrimitiveShader(gl); + // this shader is used for rendering primitives + this.primitiveShader = null; - // the next one is used for rendering triangle strips - this.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + // this shader is used for rendering triangle strips + this.complexPrimitiveShader = null; // this shader is used for the default sprite rendering - this.defaultShader = new PixiShader(gl); + this.defaultShader = null; // this shader is used for the fast sprite rendering - this.fastShader = new PixiFastShader(gl); + this.fastShader = null; // the next one is used for rendering triangle strips - this.stripShader = new StripShader(gl); - this.setShader(this.defaultShader); -}; + this.stripShader = null; + + // listen for context and update necessary shaders + var self = this; + this.renderer.on('context', function (gl) { + // this shader is used for rendering primitives + self.primitiveShader = new PrimitiveShader(gl); + + // this shader is used for rendering triangle strips + self.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + + // this shader is used for the default sprite rendering + self.defaultShader = new PixiShader(gl); + + // this shader is used for the fast sprite rendering + self.fastShader = new PixiFastShader(gl); + + // the next one is used for rendering triangle strips + self.stripShader = new StripShader(gl); + + self.setShader(self.defaultShader); + }); +} + +WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); +WebGLShaderManager.prototype.constructor = WebGLShaderManager; +module.exports = WebGLShaderManager; /** * Takes the attributes given in parameters. @@ -77,18 +104,17 @@ } // set the new attribs - for (i = 0; i < attribs.length; i++) { - var attribId = attribs[i]; - this.tempAttribState[attribId] = true; + for (var a in attribs) { + this.tempAttribState[attribs[a]] = true; } - var gl = this.gl; + var gl = this.renderer.gl; for (i = 0; i < this.attribState.length; i++) { if (this.attribState[i] !== this.tempAttribState[i]) { this.attribState[i] = this.tempAttribState[i]; - if (this.tempAttribState[i]) { + if (this.attribState[i]) { gl.enableVertexAttribArray(i); } else { @@ -104,15 +130,15 @@ * @param shader {Any} */ WebGLShaderManager.prototype.setShader = function (shader) { - if (this._currentId === shader._UID) { + if (this._currentId === shader.uuid) { return false; } - this._currentId = shader._UID; + this._currentId = shader.uuid; this.currentShader = shader; - this.gl.useProgram(shader.program); + this.renderer.gl.useProgram(shader.program); this.setAttribs(shader.attributes); return true; @@ -128,14 +154,19 @@ this.tempAttribState = null; this.primitiveShader.destroy(); + this.primitiveShader = null; this.complexPrimitiveShader.destroy(); + this.complexPrimitiveShader = null; this.defaultShader.destroy(); + this.defaultShader = null; this.fastShader.destroy(); + this.fastShader = null; this.stripShader.destroy(); + this.stripShader = null; - this.gl = null; + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderUtils.js b/src/core/renderers/webgl/utils/WebGLShaderUtils.js deleted file mode 100644 index 2942e33..0000000 --- a/src/core/renderers/webgl/utils/WebGLShaderUtils.js +++ /dev/null @@ -1,74 +0,0 @@ -var glUtils = module.exports = { - /** - * @static - * @private - */ - initDefaultShaders: function () { - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @return {Any} - */ - CompileVertexShader: function (gl, shaderSrc) { - return glUtils._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @return {Any} - */ - CompileFragmentShader: function (gl, shaderSrc) { - return glUtils._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); - }, - - /** - * @static - * @private - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @param shaderType {number} - * @return {Any} - */ - _CompileShader: function (gl, shaderSrc, shaderType) { - var src = shaderSrc.join('\n'); - var shader = gl.createShader(shaderType); - gl.shaderSource(shader, src); - gl.compileShader(shader); - - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - window.console.log(gl.getShaderInfoLog(shader)); - return null; - } - - return shader; - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param vertexSrc {Array} - * @param fragmentSrc {Array} - * @return {Any} - */ - compileProgram: function (gl, vertexSrc, fragmentSrc) { - var fragmentShader = glUtils.CompileFragmentShader(gl, fragmentSrc); - var vertexShader = glUtils.CompileVertexShader(gl, vertexSrc); - - var shaderProgram = gl.createProgram(); - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - window.console.log('Could not initialise shaders'); - } - - return shaderProgram; - } -}; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js index f18b88b..00642b1 100644 --- a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -12,7 +12,7 @@ * @class * @namespace PIXI */ -function WebGLFastSpriteBatch(gl) { +function WebGLFastSpriteBatch(renderer) { /** * @member {number} */ @@ -93,11 +93,6 @@ /** * @member {object} */ - this.renderSession = null; - - /** - * @member {object} - */ this.shader = null; /** @@ -105,7 +100,9 @@ */ this.matrix = null; - this.setContext(gl); + this.renderer = renderer; + + this.setupContext(); } WebGLFastSpriteBatch.prototype.constructor = WebGLFastSpriteBatch; @@ -116,8 +113,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -WebGLFastSpriteBatch.prototype.setContext = function (gl) { - this.gl = gl; +WebGLFastSpriteBatch.prototype.setupContext = function () { + var gl = this.renderer.gl; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -135,11 +132,11 @@ /** * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {object} + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer */ -WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderSession) { - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; +WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderer) { + this.renderer = renderer; + this.shader = renderer.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); @@ -169,9 +166,9 @@ this.currentBaseTexture = sprite.texture.baseTexture; // check blend mode - if (sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { + if (sprite.blendMode !== this.renderer.blendModeManager.currentBlendMode) { this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + this.renderer.blendModeManager.setBlendMode(sprite.blendMode); } for (var i=0,j= children.length; i 1) { gl.viewport(0, 0, filterArea.width, filterArea.height); @@ -177,7 +183,7 @@ var inputTexture = texture; var outputTexture = this.texturePool.pop(); if (!outputTexture) { - outputTexture = new FilterTexture(this.gl, this.width, this.height); + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); } outputTexture.resize(this.width, this.height); @@ -297,7 +303,7 @@ this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); + // this.renderer.shaderManager.setShader(this.defaultShader); // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); @@ -317,7 +323,8 @@ */ WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) { // use program - var gl = this.gl; + var gl = this.renderer.gl; + var shader = filter.shaders[gl.id]; if (!shader) { @@ -331,7 +338,7 @@ } // set the shader - this.renderSession.shaderManager.setShader(shader); + this.renderer.shaderManager.setShader(shader); // gl.useProgram(shader.program); @@ -361,7 +368,7 @@ // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - this.renderSession.drawCount++; + this.renderer.drawCount++; }; /** @@ -369,7 +376,7 @@ * */ WebGLFilterManager.prototype.initShaderBuffers = function () { - var gl = this.gl; + var gl = this.renderer.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); @@ -415,7 +422,7 @@ * */ WebGLFilterManager.prototype.destroy = function () { - var gl = this.gl; + var gl = this.renderer.gl; this.filterStack = null; @@ -434,4 +441,6 @@ gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLGraphics.js b/src/core/renderers/webgl/utils/WebGLGraphics.js index 86efe8f..09f540f 100644 --- a/src/core/renderers/webgl/utils/WebGLGraphics.js +++ b/src/core/renderers/webgl/utils/WebGLGraphics.js @@ -17,13 +17,14 @@ * @static * @private * @param graphics {Graphics} - * @param renderSession {object} + * @param renderer {WebGLRenderer} */ -WebGLGraphics.renderGraphics = function (graphics, renderSession) {//projection, offset) { - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader, +WebGLGraphics.renderGraphics = function (graphics, renderer) {//projection, offset) { + var gl = renderer.gl; + + var projection = renderer.projection, + offset = renderer.offset, + shader = renderer.shaderManager.primitiveShader, webGLData; if (graphics.dirty) { @@ -38,19 +39,19 @@ if (webGL.data[i].mode === 1) { webGLData = webGL.data[i]; - renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); + renderer.stencilManager.pushStencil(graphics, webGLData, renderer); // render quad.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + renderer.stencilManager.popStencil(graphics, webGLData, renderer); } else { webGLData = webGL.data[i]; - renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderer.shaderManager.primitiveShader; gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, 1); diff --git a/src/core/renderers/webgl/utils/WebGLManager.js b/src/core/renderers/webgl/utils/WebGLManager.js new file mode 100644 index 0000000..9d47b5c --- /dev/null +++ b/src/core/renderers/webgl/utils/WebGLManager.js @@ -0,0 +1,20 @@ +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLManager(renderer) { + /** + * The renderer this manager works for. + * + * @member {WebGLRenderer} + */ + this.renderer = renderer; +} + +WebGLManager.prototype.constructor = WebGLManager; +module.exports = WebGLManager; + +WebGLManager.prototype.destroy = function () { + this.renderer = null; +}; diff --git a/src/core/renderers/webgl/utils/WebGLMaskManager.js b/src/core/renderers/webgl/utils/WebGLMaskManager.js index 002c54d..6d46d85 100644 --- a/src/core/renderers/webgl/utils/WebGLMaskManager.js +++ b/src/core/renderers/webgl/utils/WebGLMaskManager.js @@ -1,59 +1,41 @@ -var WebGLGraphics = require('./WebGLGraphics'); +var WebGLManager = require('./WebGLManager'), + WebGLGraphics = require('./WebGLGraphics'); /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLMaskManager() {} +function WebGLMaskManager(renderer) { + WebGLManager.call(this, renderer); +} +WebGLMaskManager.prototype = Object.create(WebGLManager.prototype); WebGLMaskManager.prototype.constructor = WebGLMaskManager; module.exports = WebGLMaskManager; /** - * Sets the drawing context to the one given in parameter. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLMaskManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Applies the Mask and adds it to the current filter stack. * - * @param maskData {Array} - * @param renderSession {object} + * @param maskData {any[]} */ -WebGLMaskManager.prototype.pushMask = function (maskData, renderSession) { - var gl = renderSession.gl; - +WebGLMaskManager.prototype.pushMask = function (maskData) { if (maskData.dirty) { - WebGLGraphics.updateGraphics(maskData, gl); + WebGLGraphics.updateGraphics(maskData, this.renderer.gl); } - if (!maskData._webGL[gl.id].data.length) { + if (!maskData._webGL[this.renderer.gl.id].data.length) { return; } - renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); + this.renderer.stencilManager.pushStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; /** * Removes the last filter from the filter stack and doesn't return it. * - * @param maskData {Array} - * @param renderSession {object} an object containing all the useful parameters + * @param maskData {any[]} */ -WebGLMaskManager.prototype.popMask = function (maskData, renderSession) { - var gl = this.gl; - renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); -}; - -/** - * Destroys the mask stack. - * - */ -WebGLMaskManager.prototype.destroy = function () { - this.gl = null; +WebGLMaskManager.prototype.popMask = function (maskData) { + this.renderer.stencilManager.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderManager.js b/src/core/renderers/webgl/utils/WebGLShaderManager.js index 7f2c443..d02d95d 100644 --- a/src/core/renderers/webgl/utils/WebGLShaderManager.js +++ b/src/core/renderers/webgl/utils/WebGLShaderManager.js @@ -1,4 +1,5 @@ -var PrimitiveShader = require('../shaders/PrimitiveShader'), +var WebGLManager = require('./WebGLManager'), + PrimitiveShader = require('../shaders/PrimitiveShader'), ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), PixiShader = require('../shaders/PixiShader'), PixiFastShader = require('../shaders/PixiFastShader'), @@ -7,21 +8,23 @@ /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLShaderManager() { +function WebGLShaderManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.maxAttibs = 10; /** - * @member {Array} + * @member {any[]} */ this.attribState = []; /** - * @member {Array} + * @member {any[]} */ this.tempAttribState = []; @@ -30,38 +33,62 @@ } /** - * @member {Array} + * @member {any[]} */ this.stack = []; -} -WebGLShaderManager.prototype.constructor = WebGLShaderManager; -module.exports = WebGLShaderManager; + /** + * @member {number} + * @private + */ + this._currentId = -1; -/** - * Initialises the context and the properties. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLShaderManager.prototype.setContext = function (gl) { - this.gl = gl; + /** + * @member {Shader} + * @private + */ + this.currentShader = null; - // the next one is used for rendering primitives - this.primitiveShader = new PrimitiveShader(gl); + // this shader is used for rendering primitives + this.primitiveShader = null; - // the next one is used for rendering triangle strips - this.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + // this shader is used for rendering triangle strips + this.complexPrimitiveShader = null; // this shader is used for the default sprite rendering - this.defaultShader = new PixiShader(gl); + this.defaultShader = null; // this shader is used for the fast sprite rendering - this.fastShader = new PixiFastShader(gl); + this.fastShader = null; // the next one is used for rendering triangle strips - this.stripShader = new StripShader(gl); - this.setShader(this.defaultShader); -}; + this.stripShader = null; + + // listen for context and update necessary shaders + var self = this; + this.renderer.on('context', function (gl) { + // this shader is used for rendering primitives + self.primitiveShader = new PrimitiveShader(gl); + + // this shader is used for rendering triangle strips + self.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + + // this shader is used for the default sprite rendering + self.defaultShader = new PixiShader(gl); + + // this shader is used for the fast sprite rendering + self.fastShader = new PixiFastShader(gl); + + // the next one is used for rendering triangle strips + self.stripShader = new StripShader(gl); + + self.setShader(self.defaultShader); + }); +} + +WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); +WebGLShaderManager.prototype.constructor = WebGLShaderManager; +module.exports = WebGLShaderManager; /** * Takes the attributes given in parameters. @@ -77,18 +104,17 @@ } // set the new attribs - for (i = 0; i < attribs.length; i++) { - var attribId = attribs[i]; - this.tempAttribState[attribId] = true; + for (var a in attribs) { + this.tempAttribState[attribs[a]] = true; } - var gl = this.gl; + var gl = this.renderer.gl; for (i = 0; i < this.attribState.length; i++) { if (this.attribState[i] !== this.tempAttribState[i]) { this.attribState[i] = this.tempAttribState[i]; - if (this.tempAttribState[i]) { + if (this.attribState[i]) { gl.enableVertexAttribArray(i); } else { @@ -104,15 +130,15 @@ * @param shader {Any} */ WebGLShaderManager.prototype.setShader = function (shader) { - if (this._currentId === shader._UID) { + if (this._currentId === shader.uuid) { return false; } - this._currentId = shader._UID; + this._currentId = shader.uuid; this.currentShader = shader; - this.gl.useProgram(shader.program); + this.renderer.gl.useProgram(shader.program); this.setAttribs(shader.attributes); return true; @@ -128,14 +154,19 @@ this.tempAttribState = null; this.primitiveShader.destroy(); + this.primitiveShader = null; this.complexPrimitiveShader.destroy(); + this.complexPrimitiveShader = null; this.defaultShader.destroy(); + this.defaultShader = null; this.fastShader.destroy(); + this.fastShader = null; this.stripShader.destroy(); + this.stripShader = null; - this.gl = null; + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderUtils.js b/src/core/renderers/webgl/utils/WebGLShaderUtils.js deleted file mode 100644 index 2942e33..0000000 --- a/src/core/renderers/webgl/utils/WebGLShaderUtils.js +++ /dev/null @@ -1,74 +0,0 @@ -var glUtils = module.exports = { - /** - * @static - * @private - */ - initDefaultShaders: function () { - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @return {Any} - */ - CompileVertexShader: function (gl, shaderSrc) { - return glUtils._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @return {Any} - */ - CompileFragmentShader: function (gl, shaderSrc) { - return glUtils._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); - }, - - /** - * @static - * @private - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @param shaderType {number} - * @return {Any} - */ - _CompileShader: function (gl, shaderSrc, shaderType) { - var src = shaderSrc.join('\n'); - var shader = gl.createShader(shaderType); - gl.shaderSource(shader, src); - gl.compileShader(shader); - - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - window.console.log(gl.getShaderInfoLog(shader)); - return null; - } - - return shader; - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param vertexSrc {Array} - * @param fragmentSrc {Array} - * @return {Any} - */ - compileProgram: function (gl, vertexSrc, fragmentSrc) { - var fragmentShader = glUtils.CompileFragmentShader(gl, fragmentSrc); - var vertexShader = glUtils.CompileVertexShader(gl, vertexSrc); - - var shaderProgram = gl.createProgram(); - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - window.console.log('Could not initialise shaders'); - } - - return shaderProgram; - } -}; diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index 07f99c3..e0984c4 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -481,7 +481,7 @@ nextShader = sprite.shader || this.defaultShader; blendSwap = currentBlendMode !== nextBlendMode; - shaderSwap = currentShader !== nextShader; // should I use _UIDS??? + shaderSwap = currentShader !== nextShader; // should I use uuidS??? if (currentBaseTexture !== nextTexture || blendSwap || shaderSwap) { this.renderBatch(currentBaseTexture, batchSize, start); diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js index f18b88b..00642b1 100644 --- a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -12,7 +12,7 @@ * @class * @namespace PIXI */ -function WebGLFastSpriteBatch(gl) { +function WebGLFastSpriteBatch(renderer) { /** * @member {number} */ @@ -93,11 +93,6 @@ /** * @member {object} */ - this.renderSession = null; - - /** - * @member {object} - */ this.shader = null; /** @@ -105,7 +100,9 @@ */ this.matrix = null; - this.setContext(gl); + this.renderer = renderer; + + this.setupContext(); } WebGLFastSpriteBatch.prototype.constructor = WebGLFastSpriteBatch; @@ -116,8 +113,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -WebGLFastSpriteBatch.prototype.setContext = function (gl) { - this.gl = gl; +WebGLFastSpriteBatch.prototype.setupContext = function () { + var gl = this.renderer.gl; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -135,11 +132,11 @@ /** * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {object} + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer */ -WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderSession) { - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; +WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderer) { + this.renderer = renderer; + this.shader = renderer.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); @@ -169,9 +166,9 @@ this.currentBaseTexture = sprite.texture.baseTexture; // check blend mode - if (sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { + if (sprite.blendMode !== this.renderer.blendModeManager.currentBlendMode) { this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + this.renderer.blendModeManager.setBlendMode(sprite.blendMode); } for (var i=0,j= children.length; i 1) { gl.viewport(0, 0, filterArea.width, filterArea.height); @@ -177,7 +183,7 @@ var inputTexture = texture; var outputTexture = this.texturePool.pop(); if (!outputTexture) { - outputTexture = new FilterTexture(this.gl, this.width, this.height); + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); } outputTexture.resize(this.width, this.height); @@ -297,7 +303,7 @@ this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); + // this.renderer.shaderManager.setShader(this.defaultShader); // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); @@ -317,7 +323,8 @@ */ WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) { // use program - var gl = this.gl; + var gl = this.renderer.gl; + var shader = filter.shaders[gl.id]; if (!shader) { @@ -331,7 +338,7 @@ } // set the shader - this.renderSession.shaderManager.setShader(shader); + this.renderer.shaderManager.setShader(shader); // gl.useProgram(shader.program); @@ -361,7 +368,7 @@ // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - this.renderSession.drawCount++; + this.renderer.drawCount++; }; /** @@ -369,7 +376,7 @@ * */ WebGLFilterManager.prototype.initShaderBuffers = function () { - var gl = this.gl; + var gl = this.renderer.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); @@ -415,7 +422,7 @@ * */ WebGLFilterManager.prototype.destroy = function () { - var gl = this.gl; + var gl = this.renderer.gl; this.filterStack = null; @@ -434,4 +441,6 @@ gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLGraphics.js b/src/core/renderers/webgl/utils/WebGLGraphics.js index 86efe8f..09f540f 100644 --- a/src/core/renderers/webgl/utils/WebGLGraphics.js +++ b/src/core/renderers/webgl/utils/WebGLGraphics.js @@ -17,13 +17,14 @@ * @static * @private * @param graphics {Graphics} - * @param renderSession {object} + * @param renderer {WebGLRenderer} */ -WebGLGraphics.renderGraphics = function (graphics, renderSession) {//projection, offset) { - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader, +WebGLGraphics.renderGraphics = function (graphics, renderer) {//projection, offset) { + var gl = renderer.gl; + + var projection = renderer.projection, + offset = renderer.offset, + shader = renderer.shaderManager.primitiveShader, webGLData; if (graphics.dirty) { @@ -38,19 +39,19 @@ if (webGL.data[i].mode === 1) { webGLData = webGL.data[i]; - renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); + renderer.stencilManager.pushStencil(graphics, webGLData, renderer); // render quad.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + renderer.stencilManager.popStencil(graphics, webGLData, renderer); } else { webGLData = webGL.data[i]; - renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderer.shaderManager.primitiveShader; gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, 1); diff --git a/src/core/renderers/webgl/utils/WebGLManager.js b/src/core/renderers/webgl/utils/WebGLManager.js new file mode 100644 index 0000000..9d47b5c --- /dev/null +++ b/src/core/renderers/webgl/utils/WebGLManager.js @@ -0,0 +1,20 @@ +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLManager(renderer) { + /** + * The renderer this manager works for. + * + * @member {WebGLRenderer} + */ + this.renderer = renderer; +} + +WebGLManager.prototype.constructor = WebGLManager; +module.exports = WebGLManager; + +WebGLManager.prototype.destroy = function () { + this.renderer = null; +}; diff --git a/src/core/renderers/webgl/utils/WebGLMaskManager.js b/src/core/renderers/webgl/utils/WebGLMaskManager.js index 002c54d..6d46d85 100644 --- a/src/core/renderers/webgl/utils/WebGLMaskManager.js +++ b/src/core/renderers/webgl/utils/WebGLMaskManager.js @@ -1,59 +1,41 @@ -var WebGLGraphics = require('./WebGLGraphics'); +var WebGLManager = require('./WebGLManager'), + WebGLGraphics = require('./WebGLGraphics'); /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLMaskManager() {} +function WebGLMaskManager(renderer) { + WebGLManager.call(this, renderer); +} +WebGLMaskManager.prototype = Object.create(WebGLManager.prototype); WebGLMaskManager.prototype.constructor = WebGLMaskManager; module.exports = WebGLMaskManager; /** - * Sets the drawing context to the one given in parameter. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLMaskManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Applies the Mask and adds it to the current filter stack. * - * @param maskData {Array} - * @param renderSession {object} + * @param maskData {any[]} */ -WebGLMaskManager.prototype.pushMask = function (maskData, renderSession) { - var gl = renderSession.gl; - +WebGLMaskManager.prototype.pushMask = function (maskData) { if (maskData.dirty) { - WebGLGraphics.updateGraphics(maskData, gl); + WebGLGraphics.updateGraphics(maskData, this.renderer.gl); } - if (!maskData._webGL[gl.id].data.length) { + if (!maskData._webGL[this.renderer.gl.id].data.length) { return; } - renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); + this.renderer.stencilManager.pushStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; /** * Removes the last filter from the filter stack and doesn't return it. * - * @param maskData {Array} - * @param renderSession {object} an object containing all the useful parameters + * @param maskData {any[]} */ -WebGLMaskManager.prototype.popMask = function (maskData, renderSession) { - var gl = this.gl; - renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); -}; - -/** - * Destroys the mask stack. - * - */ -WebGLMaskManager.prototype.destroy = function () { - this.gl = null; +WebGLMaskManager.prototype.popMask = function (maskData) { + this.renderer.stencilManager.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderManager.js b/src/core/renderers/webgl/utils/WebGLShaderManager.js index 7f2c443..d02d95d 100644 --- a/src/core/renderers/webgl/utils/WebGLShaderManager.js +++ b/src/core/renderers/webgl/utils/WebGLShaderManager.js @@ -1,4 +1,5 @@ -var PrimitiveShader = require('../shaders/PrimitiveShader'), +var WebGLManager = require('./WebGLManager'), + PrimitiveShader = require('../shaders/PrimitiveShader'), ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), PixiShader = require('../shaders/PixiShader'), PixiFastShader = require('../shaders/PixiFastShader'), @@ -7,21 +8,23 @@ /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLShaderManager() { +function WebGLShaderManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.maxAttibs = 10; /** - * @member {Array} + * @member {any[]} */ this.attribState = []; /** - * @member {Array} + * @member {any[]} */ this.tempAttribState = []; @@ -30,38 +33,62 @@ } /** - * @member {Array} + * @member {any[]} */ this.stack = []; -} -WebGLShaderManager.prototype.constructor = WebGLShaderManager; -module.exports = WebGLShaderManager; + /** + * @member {number} + * @private + */ + this._currentId = -1; -/** - * Initialises the context and the properties. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLShaderManager.prototype.setContext = function (gl) { - this.gl = gl; + /** + * @member {Shader} + * @private + */ + this.currentShader = null; - // the next one is used for rendering primitives - this.primitiveShader = new PrimitiveShader(gl); + // this shader is used for rendering primitives + this.primitiveShader = null; - // the next one is used for rendering triangle strips - this.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + // this shader is used for rendering triangle strips + this.complexPrimitiveShader = null; // this shader is used for the default sprite rendering - this.defaultShader = new PixiShader(gl); + this.defaultShader = null; // this shader is used for the fast sprite rendering - this.fastShader = new PixiFastShader(gl); + this.fastShader = null; // the next one is used for rendering triangle strips - this.stripShader = new StripShader(gl); - this.setShader(this.defaultShader); -}; + this.stripShader = null; + + // listen for context and update necessary shaders + var self = this; + this.renderer.on('context', function (gl) { + // this shader is used for rendering primitives + self.primitiveShader = new PrimitiveShader(gl); + + // this shader is used for rendering triangle strips + self.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + + // this shader is used for the default sprite rendering + self.defaultShader = new PixiShader(gl); + + // this shader is used for the fast sprite rendering + self.fastShader = new PixiFastShader(gl); + + // the next one is used for rendering triangle strips + self.stripShader = new StripShader(gl); + + self.setShader(self.defaultShader); + }); +} + +WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); +WebGLShaderManager.prototype.constructor = WebGLShaderManager; +module.exports = WebGLShaderManager; /** * Takes the attributes given in parameters. @@ -77,18 +104,17 @@ } // set the new attribs - for (i = 0; i < attribs.length; i++) { - var attribId = attribs[i]; - this.tempAttribState[attribId] = true; + for (var a in attribs) { + this.tempAttribState[attribs[a]] = true; } - var gl = this.gl; + var gl = this.renderer.gl; for (i = 0; i < this.attribState.length; i++) { if (this.attribState[i] !== this.tempAttribState[i]) { this.attribState[i] = this.tempAttribState[i]; - if (this.tempAttribState[i]) { + if (this.attribState[i]) { gl.enableVertexAttribArray(i); } else { @@ -104,15 +130,15 @@ * @param shader {Any} */ WebGLShaderManager.prototype.setShader = function (shader) { - if (this._currentId === shader._UID) { + if (this._currentId === shader.uuid) { return false; } - this._currentId = shader._UID; + this._currentId = shader.uuid; this.currentShader = shader; - this.gl.useProgram(shader.program); + this.renderer.gl.useProgram(shader.program); this.setAttribs(shader.attributes); return true; @@ -128,14 +154,19 @@ this.tempAttribState = null; this.primitiveShader.destroy(); + this.primitiveShader = null; this.complexPrimitiveShader.destroy(); + this.complexPrimitiveShader = null; this.defaultShader.destroy(); + this.defaultShader = null; this.fastShader.destroy(); + this.fastShader = null; this.stripShader.destroy(); + this.stripShader = null; - this.gl = null; + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderUtils.js b/src/core/renderers/webgl/utils/WebGLShaderUtils.js deleted file mode 100644 index 2942e33..0000000 --- a/src/core/renderers/webgl/utils/WebGLShaderUtils.js +++ /dev/null @@ -1,74 +0,0 @@ -var glUtils = module.exports = { - /** - * @static - * @private - */ - initDefaultShaders: function () { - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @return {Any} - */ - CompileVertexShader: function (gl, shaderSrc) { - return glUtils._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @return {Any} - */ - CompileFragmentShader: function (gl, shaderSrc) { - return glUtils._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); - }, - - /** - * @static - * @private - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @param shaderType {number} - * @return {Any} - */ - _CompileShader: function (gl, shaderSrc, shaderType) { - var src = shaderSrc.join('\n'); - var shader = gl.createShader(shaderType); - gl.shaderSource(shader, src); - gl.compileShader(shader); - - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - window.console.log(gl.getShaderInfoLog(shader)); - return null; - } - - return shader; - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param vertexSrc {Array} - * @param fragmentSrc {Array} - * @return {Any} - */ - compileProgram: function (gl, vertexSrc, fragmentSrc) { - var fragmentShader = glUtils.CompileFragmentShader(gl, fragmentSrc); - var vertexShader = glUtils.CompileVertexShader(gl, vertexSrc); - - var shaderProgram = gl.createProgram(); - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - window.console.log('Could not initialise shaders'); - } - - return shaderProgram; - } -}; diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index 07f99c3..e0984c4 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -481,7 +481,7 @@ nextShader = sprite.shader || this.defaultShader; blendSwap = currentBlendMode !== nextBlendMode; - shaderSwap = currentShader !== nextShader; // should I use _UIDS??? + shaderSwap = currentShader !== nextShader; // should I use uuidS??? if (currentBaseTexture !== nextTexture || blendSwap || shaderSwap) { this.renderBatch(currentBaseTexture, batchSize, start); diff --git a/src/core/renderers/webgl/utils/WebGLStencilManager.js b/src/core/renderers/webgl/utils/WebGLStencilManager.js index ddba5a1..0bedae6 100644 --- a/src/core/renderers/webgl/utils/WebGLStencilManager.js +++ b/src/core/renderers/webgl/utils/WebGLStencilManager.js @@ -1,38 +1,33 @@ -var utils = require('../../../utils'); +var WebGLManager = require('./WebGLManager'), + utils = require('../../../utils'); /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLStencilManager() { +function WebGLStencilManager(renderer) { + WebGLManager.call(this, renderer); + this.stencilStack = []; this.reverse = true; this.count = 0; } +WebGLStencilManager.prototype = Object.create(WebGLManager.prototype); WebGLStencilManager.prototype.constructor = WebGLStencilManager; module.exports = WebGLStencilManager; /** - * Sets the drawing context to the one given in parameter. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLStencilManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Applies the Mask and adds it to the current filter stack. * * @param graphics {Graphics} - * @param webGLData {Array} - * @param renderSession {object} + * @param webGLData {any[]} */ -WebGLStencilManager.prototype.pushStencil = function (graphics, webGLData, renderSession) { - var gl = this.gl; - this.bindGraphics(graphics, webGLData, renderSession); +WebGLStencilManager.prototype.pushStencil = function (graphics, webGLData) { + var gl = this.renderer.gl; + + this.bindGraphics(graphics, webGLData, this.renderer); if (this.stencilStack.length === 0) { gl.enable(gl.STENCIL_TEST); @@ -107,25 +102,24 @@ * * @param graphics {Graphics} * @param webGLData {Array} - * @param renderSession {object} */ -WebGLStencilManager.prototype.bindGraphics = function (graphics, webGLData, renderSession) { +WebGLStencilManager.prototype.bindGraphics = function (graphics, webGLData) { //if (this._currentGraphics === graphics)return; this._currentGraphics = graphics; - var gl = this.gl; + var gl = this.renderer.gl; // bind the graphics object.. - var projection = renderSession.projection, - offset = renderSession.offset, - shader;// = renderSession.shaderManager.primitiveShader; + var projection = this.renderer.projection, + offset = this.renderer.offset, + shader;// = this.renderer.shaderManager.primitiveShader; if (webGLData.mode === 1) { - shader = renderSession.shaderManager.complexPrimitiveShader; + shader = this.renderer.shaderManager.complexPrimitiveShader; - renderSession.shaderManager.setShader( shader ); + this.renderer.shaderManager.setShader(shader); - gl.uniform1f(shader.flipY, renderSession.flipY); + gl.uniform1f(shader.flipY, this.renderer.flipY); gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); @@ -147,13 +141,13 @@ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); } else { - //renderSession.shaderManager.activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; - renderSession.shaderManager.setShader( shader ); + //this.renderer.shaderManager.activatePrimitiveShader(); + shader = this.renderer.shaderManager.primitiveShader; + this.renderer.shaderManager.setShader( shader ); gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); - gl.uniform1f(shader.flipY, renderSession.flipY); + gl.uniform1f(shader.flipY, this.renderer.flipY); gl.uniform2f(shader.projectionVector, projection.x, -projection.y); gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); @@ -174,10 +168,10 @@ /** * @param graphics {Graphics} * @param webGLData {Array} - * @param renderSession {object} */ -WebGLStencilManager.prototype.popStencil = function (graphics, webGLData, renderSession) { - var gl = this.gl; +WebGLStencilManager.prototype.popStencil = function (graphics, webGLData) { + var gl = this.renderer.gl; + this.stencilStack.pop(); this.count--; @@ -191,7 +185,7 @@ var level = this.count; - this.bindGraphics(graphics, webGLData, renderSession); + this.bindGraphics(graphics, webGLData, this.renderer); gl.colorMask(false, false, false, false); @@ -257,6 +251,6 @@ * */ WebGLStencilManager.prototype.destroy = function () { + this.renderer = null; this.stencilStack = null; - this.gl = null; }; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js index f18b88b..00642b1 100644 --- a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -12,7 +12,7 @@ * @class * @namespace PIXI */ -function WebGLFastSpriteBatch(gl) { +function WebGLFastSpriteBatch(renderer) { /** * @member {number} */ @@ -93,11 +93,6 @@ /** * @member {object} */ - this.renderSession = null; - - /** - * @member {object} - */ this.shader = null; /** @@ -105,7 +100,9 @@ */ this.matrix = null; - this.setContext(gl); + this.renderer = renderer; + + this.setupContext(); } WebGLFastSpriteBatch.prototype.constructor = WebGLFastSpriteBatch; @@ -116,8 +113,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -WebGLFastSpriteBatch.prototype.setContext = function (gl) { - this.gl = gl; +WebGLFastSpriteBatch.prototype.setupContext = function () { + var gl = this.renderer.gl; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -135,11 +132,11 @@ /** * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {object} + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer */ -WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderSession) { - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; +WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderer) { + this.renderer = renderer; + this.shader = renderer.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); @@ -169,9 +166,9 @@ this.currentBaseTexture = sprite.texture.baseTexture; // check blend mode - if (sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { + if (sprite.blendMode !== this.renderer.blendModeManager.currentBlendMode) { this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + this.renderer.blendModeManager.setBlendMode(sprite.blendMode); } for (var i=0,j= children.length; i 1) { gl.viewport(0, 0, filterArea.width, filterArea.height); @@ -177,7 +183,7 @@ var inputTexture = texture; var outputTexture = this.texturePool.pop(); if (!outputTexture) { - outputTexture = new FilterTexture(this.gl, this.width, this.height); + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); } outputTexture.resize(this.width, this.height); @@ -297,7 +303,7 @@ this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); + // this.renderer.shaderManager.setShader(this.defaultShader); // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); @@ -317,7 +323,8 @@ */ WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) { // use program - var gl = this.gl; + var gl = this.renderer.gl; + var shader = filter.shaders[gl.id]; if (!shader) { @@ -331,7 +338,7 @@ } // set the shader - this.renderSession.shaderManager.setShader(shader); + this.renderer.shaderManager.setShader(shader); // gl.useProgram(shader.program); @@ -361,7 +368,7 @@ // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - this.renderSession.drawCount++; + this.renderer.drawCount++; }; /** @@ -369,7 +376,7 @@ * */ WebGLFilterManager.prototype.initShaderBuffers = function () { - var gl = this.gl; + var gl = this.renderer.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); @@ -415,7 +422,7 @@ * */ WebGLFilterManager.prototype.destroy = function () { - var gl = this.gl; + var gl = this.renderer.gl; this.filterStack = null; @@ -434,4 +441,6 @@ gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLGraphics.js b/src/core/renderers/webgl/utils/WebGLGraphics.js index 86efe8f..09f540f 100644 --- a/src/core/renderers/webgl/utils/WebGLGraphics.js +++ b/src/core/renderers/webgl/utils/WebGLGraphics.js @@ -17,13 +17,14 @@ * @static * @private * @param graphics {Graphics} - * @param renderSession {object} + * @param renderer {WebGLRenderer} */ -WebGLGraphics.renderGraphics = function (graphics, renderSession) {//projection, offset) { - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader, +WebGLGraphics.renderGraphics = function (graphics, renderer) {//projection, offset) { + var gl = renderer.gl; + + var projection = renderer.projection, + offset = renderer.offset, + shader = renderer.shaderManager.primitiveShader, webGLData; if (graphics.dirty) { @@ -38,19 +39,19 @@ if (webGL.data[i].mode === 1) { webGLData = webGL.data[i]; - renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); + renderer.stencilManager.pushStencil(graphics, webGLData, renderer); // render quad.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + renderer.stencilManager.popStencil(graphics, webGLData, renderer); } else { webGLData = webGL.data[i]; - renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderer.shaderManager.primitiveShader; gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, 1); diff --git a/src/core/renderers/webgl/utils/WebGLManager.js b/src/core/renderers/webgl/utils/WebGLManager.js new file mode 100644 index 0000000..9d47b5c --- /dev/null +++ b/src/core/renderers/webgl/utils/WebGLManager.js @@ -0,0 +1,20 @@ +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLManager(renderer) { + /** + * The renderer this manager works for. + * + * @member {WebGLRenderer} + */ + this.renderer = renderer; +} + +WebGLManager.prototype.constructor = WebGLManager; +module.exports = WebGLManager; + +WebGLManager.prototype.destroy = function () { + this.renderer = null; +}; diff --git a/src/core/renderers/webgl/utils/WebGLMaskManager.js b/src/core/renderers/webgl/utils/WebGLMaskManager.js index 002c54d..6d46d85 100644 --- a/src/core/renderers/webgl/utils/WebGLMaskManager.js +++ b/src/core/renderers/webgl/utils/WebGLMaskManager.js @@ -1,59 +1,41 @@ -var WebGLGraphics = require('./WebGLGraphics'); +var WebGLManager = require('./WebGLManager'), + WebGLGraphics = require('./WebGLGraphics'); /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLMaskManager() {} +function WebGLMaskManager(renderer) { + WebGLManager.call(this, renderer); +} +WebGLMaskManager.prototype = Object.create(WebGLManager.prototype); WebGLMaskManager.prototype.constructor = WebGLMaskManager; module.exports = WebGLMaskManager; /** - * Sets the drawing context to the one given in parameter. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLMaskManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Applies the Mask and adds it to the current filter stack. * - * @param maskData {Array} - * @param renderSession {object} + * @param maskData {any[]} */ -WebGLMaskManager.prototype.pushMask = function (maskData, renderSession) { - var gl = renderSession.gl; - +WebGLMaskManager.prototype.pushMask = function (maskData) { if (maskData.dirty) { - WebGLGraphics.updateGraphics(maskData, gl); + WebGLGraphics.updateGraphics(maskData, this.renderer.gl); } - if (!maskData._webGL[gl.id].data.length) { + if (!maskData._webGL[this.renderer.gl.id].data.length) { return; } - renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); + this.renderer.stencilManager.pushStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; /** * Removes the last filter from the filter stack and doesn't return it. * - * @param maskData {Array} - * @param renderSession {object} an object containing all the useful parameters + * @param maskData {any[]} */ -WebGLMaskManager.prototype.popMask = function (maskData, renderSession) { - var gl = this.gl; - renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); -}; - -/** - * Destroys the mask stack. - * - */ -WebGLMaskManager.prototype.destroy = function () { - this.gl = null; +WebGLMaskManager.prototype.popMask = function (maskData) { + this.renderer.stencilManager.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderManager.js b/src/core/renderers/webgl/utils/WebGLShaderManager.js index 7f2c443..d02d95d 100644 --- a/src/core/renderers/webgl/utils/WebGLShaderManager.js +++ b/src/core/renderers/webgl/utils/WebGLShaderManager.js @@ -1,4 +1,5 @@ -var PrimitiveShader = require('../shaders/PrimitiveShader'), +var WebGLManager = require('./WebGLManager'), + PrimitiveShader = require('../shaders/PrimitiveShader'), ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), PixiShader = require('../shaders/PixiShader'), PixiFastShader = require('../shaders/PixiFastShader'), @@ -7,21 +8,23 @@ /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLShaderManager() { +function WebGLShaderManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.maxAttibs = 10; /** - * @member {Array} + * @member {any[]} */ this.attribState = []; /** - * @member {Array} + * @member {any[]} */ this.tempAttribState = []; @@ -30,38 +33,62 @@ } /** - * @member {Array} + * @member {any[]} */ this.stack = []; -} -WebGLShaderManager.prototype.constructor = WebGLShaderManager; -module.exports = WebGLShaderManager; + /** + * @member {number} + * @private + */ + this._currentId = -1; -/** - * Initialises the context and the properties. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLShaderManager.prototype.setContext = function (gl) { - this.gl = gl; + /** + * @member {Shader} + * @private + */ + this.currentShader = null; - // the next one is used for rendering primitives - this.primitiveShader = new PrimitiveShader(gl); + // this shader is used for rendering primitives + this.primitiveShader = null; - // the next one is used for rendering triangle strips - this.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + // this shader is used for rendering triangle strips + this.complexPrimitiveShader = null; // this shader is used for the default sprite rendering - this.defaultShader = new PixiShader(gl); + this.defaultShader = null; // this shader is used for the fast sprite rendering - this.fastShader = new PixiFastShader(gl); + this.fastShader = null; // the next one is used for rendering triangle strips - this.stripShader = new StripShader(gl); - this.setShader(this.defaultShader); -}; + this.stripShader = null; + + // listen for context and update necessary shaders + var self = this; + this.renderer.on('context', function (gl) { + // this shader is used for rendering primitives + self.primitiveShader = new PrimitiveShader(gl); + + // this shader is used for rendering triangle strips + self.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + + // this shader is used for the default sprite rendering + self.defaultShader = new PixiShader(gl); + + // this shader is used for the fast sprite rendering + self.fastShader = new PixiFastShader(gl); + + // the next one is used for rendering triangle strips + self.stripShader = new StripShader(gl); + + self.setShader(self.defaultShader); + }); +} + +WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); +WebGLShaderManager.prototype.constructor = WebGLShaderManager; +module.exports = WebGLShaderManager; /** * Takes the attributes given in parameters. @@ -77,18 +104,17 @@ } // set the new attribs - for (i = 0; i < attribs.length; i++) { - var attribId = attribs[i]; - this.tempAttribState[attribId] = true; + for (var a in attribs) { + this.tempAttribState[attribs[a]] = true; } - var gl = this.gl; + var gl = this.renderer.gl; for (i = 0; i < this.attribState.length; i++) { if (this.attribState[i] !== this.tempAttribState[i]) { this.attribState[i] = this.tempAttribState[i]; - if (this.tempAttribState[i]) { + if (this.attribState[i]) { gl.enableVertexAttribArray(i); } else { @@ -104,15 +130,15 @@ * @param shader {Any} */ WebGLShaderManager.prototype.setShader = function (shader) { - if (this._currentId === shader._UID) { + if (this._currentId === shader.uuid) { return false; } - this._currentId = shader._UID; + this._currentId = shader.uuid; this.currentShader = shader; - this.gl.useProgram(shader.program); + this.renderer.gl.useProgram(shader.program); this.setAttribs(shader.attributes); return true; @@ -128,14 +154,19 @@ this.tempAttribState = null; this.primitiveShader.destroy(); + this.primitiveShader = null; this.complexPrimitiveShader.destroy(); + this.complexPrimitiveShader = null; this.defaultShader.destroy(); + this.defaultShader = null; this.fastShader.destroy(); + this.fastShader = null; this.stripShader.destroy(); + this.stripShader = null; - this.gl = null; + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderUtils.js b/src/core/renderers/webgl/utils/WebGLShaderUtils.js deleted file mode 100644 index 2942e33..0000000 --- a/src/core/renderers/webgl/utils/WebGLShaderUtils.js +++ /dev/null @@ -1,74 +0,0 @@ -var glUtils = module.exports = { - /** - * @static - * @private - */ - initDefaultShaders: function () { - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @return {Any} - */ - CompileVertexShader: function (gl, shaderSrc) { - return glUtils._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @return {Any} - */ - CompileFragmentShader: function (gl, shaderSrc) { - return glUtils._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); - }, - - /** - * @static - * @private - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @param shaderType {number} - * @return {Any} - */ - _CompileShader: function (gl, shaderSrc, shaderType) { - var src = shaderSrc.join('\n'); - var shader = gl.createShader(shaderType); - gl.shaderSource(shader, src); - gl.compileShader(shader); - - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - window.console.log(gl.getShaderInfoLog(shader)); - return null; - } - - return shader; - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param vertexSrc {Array} - * @param fragmentSrc {Array} - * @return {Any} - */ - compileProgram: function (gl, vertexSrc, fragmentSrc) { - var fragmentShader = glUtils.CompileFragmentShader(gl, fragmentSrc); - var vertexShader = glUtils.CompileVertexShader(gl, vertexSrc); - - var shaderProgram = gl.createProgram(); - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - window.console.log('Could not initialise shaders'); - } - - return shaderProgram; - } -}; diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index 07f99c3..e0984c4 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -481,7 +481,7 @@ nextShader = sprite.shader || this.defaultShader; blendSwap = currentBlendMode !== nextBlendMode; - shaderSwap = currentShader !== nextShader; // should I use _UIDS??? + shaderSwap = currentShader !== nextShader; // should I use uuidS??? if (currentBaseTexture !== nextTexture || blendSwap || shaderSwap) { this.renderBatch(currentBaseTexture, batchSize, start); diff --git a/src/core/renderers/webgl/utils/WebGLStencilManager.js b/src/core/renderers/webgl/utils/WebGLStencilManager.js index ddba5a1..0bedae6 100644 --- a/src/core/renderers/webgl/utils/WebGLStencilManager.js +++ b/src/core/renderers/webgl/utils/WebGLStencilManager.js @@ -1,38 +1,33 @@ -var utils = require('../../../utils'); +var WebGLManager = require('./WebGLManager'), + utils = require('../../../utils'); /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLStencilManager() { +function WebGLStencilManager(renderer) { + WebGLManager.call(this, renderer); + this.stencilStack = []; this.reverse = true; this.count = 0; } +WebGLStencilManager.prototype = Object.create(WebGLManager.prototype); WebGLStencilManager.prototype.constructor = WebGLStencilManager; module.exports = WebGLStencilManager; /** - * Sets the drawing context to the one given in parameter. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLStencilManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Applies the Mask and adds it to the current filter stack. * * @param graphics {Graphics} - * @param webGLData {Array} - * @param renderSession {object} + * @param webGLData {any[]} */ -WebGLStencilManager.prototype.pushStencil = function (graphics, webGLData, renderSession) { - var gl = this.gl; - this.bindGraphics(graphics, webGLData, renderSession); +WebGLStencilManager.prototype.pushStencil = function (graphics, webGLData) { + var gl = this.renderer.gl; + + this.bindGraphics(graphics, webGLData, this.renderer); if (this.stencilStack.length === 0) { gl.enable(gl.STENCIL_TEST); @@ -107,25 +102,24 @@ * * @param graphics {Graphics} * @param webGLData {Array} - * @param renderSession {object} */ -WebGLStencilManager.prototype.bindGraphics = function (graphics, webGLData, renderSession) { +WebGLStencilManager.prototype.bindGraphics = function (graphics, webGLData) { //if (this._currentGraphics === graphics)return; this._currentGraphics = graphics; - var gl = this.gl; + var gl = this.renderer.gl; // bind the graphics object.. - var projection = renderSession.projection, - offset = renderSession.offset, - shader;// = renderSession.shaderManager.primitiveShader; + var projection = this.renderer.projection, + offset = this.renderer.offset, + shader;// = this.renderer.shaderManager.primitiveShader; if (webGLData.mode === 1) { - shader = renderSession.shaderManager.complexPrimitiveShader; + shader = this.renderer.shaderManager.complexPrimitiveShader; - renderSession.shaderManager.setShader( shader ); + this.renderer.shaderManager.setShader(shader); - gl.uniform1f(shader.flipY, renderSession.flipY); + gl.uniform1f(shader.flipY, this.renderer.flipY); gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); @@ -147,13 +141,13 @@ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); } else { - //renderSession.shaderManager.activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; - renderSession.shaderManager.setShader( shader ); + //this.renderer.shaderManager.activatePrimitiveShader(); + shader = this.renderer.shaderManager.primitiveShader; + this.renderer.shaderManager.setShader( shader ); gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); - gl.uniform1f(shader.flipY, renderSession.flipY); + gl.uniform1f(shader.flipY, this.renderer.flipY); gl.uniform2f(shader.projectionVector, projection.x, -projection.y); gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); @@ -174,10 +168,10 @@ /** * @param graphics {Graphics} * @param webGLData {Array} - * @param renderSession {object} */ -WebGLStencilManager.prototype.popStencil = function (graphics, webGLData, renderSession) { - var gl = this.gl; +WebGLStencilManager.prototype.popStencil = function (graphics, webGLData) { + var gl = this.renderer.gl; + this.stencilStack.pop(); this.count--; @@ -191,7 +185,7 @@ var level = this.count; - this.bindGraphics(graphics, webGLData, renderSession); + this.bindGraphics(graphics, webGLData, this.renderer); gl.colorMask(false, false, false, false); @@ -257,6 +251,6 @@ * */ WebGLStencilManager.prototype.destroy = function () { + this.renderer = null; this.stencilStack = null; - this.gl = null; }; diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 26565cd..121a583 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -57,7 +57,8 @@ */ this.source = source; - this._UID = utils.uuid(); + // ID for the base texture + this.uuid = utils.uuid(); /** * Controls if RGB channels should be pre-multiplied by Alpha (WebGL only) @@ -88,10 +89,10 @@ // TODO - this needs to be addressed /** - * @member {Array} + * @member {object} * @private */ - this._dirty = [true, true, true, true]; + this._dirty = {}; if (!source) { return; diff --git a/src/core/const.js b/src/core/const.js index c0f5b18..2803e5b 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -117,6 +117,7 @@ * @property {boolean} defaultRenderOptions.antialias=false * @property {boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {number} defaultRenderOptions.resolution=1 + * @property {number} defaultRenderOptions.backgroundColor=0x000000 * @property {boolean} defaultRenderOptions.clearBeforeRender=true * @property {boolean} defaultRenderOptions.autoResize=false */ @@ -126,6 +127,7 @@ antialias: false, autoResize: false, transparent: false, + backgroundColor: 0x000000, clearBeforeRender: true, preserveDrawingBuffer: false } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 86a59d4..bfd59cf 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -23,14 +23,14 @@ * * @member {Point} */ - this.scale = new math.Point(1,1);//{x:1, y:1}; + this.scale = new math.Point(1, 1); /** * The pivot point of the displayObject that it rotates around * * @member {Point} */ - this.pivot = new math.Point(0,0); + this.pivot = new math.Point(0, 0); /** * The rotation of the object in radians. @@ -55,22 +55,6 @@ this.visible = true; /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager - * will use (as it will not need to hit test all the children) - * - * @member {Rectangle|Circle|Ellipse|Polygon} - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @member {boolean} - */ - this.buttonMode = false; - - /** * Can this object be rendered, if false the object will not be drawn but the updateTransform * methods will still be called. * @@ -87,15 +71,6 @@ this.parent = null; /** - * The stage the display object is connected to, or undefined if it is not - * connected to the stage. - * - * @member {Stage} - * @readOnly - */ - this.stage = null; - - /** * The multiplied alpha of the displayObject * * @member {number} @@ -104,34 +79,22 @@ this.worldAlpha = 1; /** - * Whether or not the object is interactive, do not toggle directly! use - * the `interactive` property - * - * @member {Boolean} - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this - * the element must have interaction = true and buttonMode = true - * - * @member {string} - * - */ - this.defaultCursor = 'pointer'; - - /** * Current transform of the object based on world (parent) factors * * @member {Matrix} * @readOnly - * @private */ this.worldTransform = new math.Matrix(); /** + * The area the filter is applied to. This is used as more of an optimisation + * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle + * + * @member {Rectangle} + */ + this.filterArea = null; + + /** * cached sin rotation and cos rotation * * @member {number} @@ -148,14 +111,6 @@ this._cr = 1; /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @member {Rectangle} - */ - this.filterArea = null; // new math.Rectangle(0,0,1,1); - - /** * The original, cached bounds of the object * * @member {Rectangle} @@ -194,151 +149,6 @@ * @private */ this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * - * @method mouseover - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseover = null; - - /** - * A callback that is used when the users mouse leaves the displayObject - * - * @method mouseout - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseout = null; - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * - * @method click - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.click = null; - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * - * @method mousedown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mousedown = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * - * @method mouseup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseup = null; - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * but is no longer over the displayObject for this callback to be fired, the mouse's left button must - * have been pressed down over the displayObject - * - * @method mouseupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.mouseupoutside = null; - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * - * @method rightclick - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightclick = null; - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * - * @method rightdown - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightdown = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * - * @method rightup - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightup = null; - - /** - * A callback that is used when the user releases the mouse's right button that was over the - * displayObject but is no longer over the displayObject for this callback to be fired, the mouse's - * right button must have been pressed down over the displayObject - * - * @method rightupoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.rightupoutside = null; - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * - * @method tap - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.tap = null; - - /** - * A callback that is used when the user touches over the displayObject - * - * @method touchstart - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchstart = null; - - /** - * A callback that is used when the user releases a touch over the displayObject - * - * @method touchend - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchend = null; - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * - * @method touchendoutside - * @memberof DisplayObject# - * @param interactionData {InteractionData} - */ - this.touchendoutside = null; } // constructor @@ -377,33 +187,11 @@ }, /** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @member {boolean} - * @default false - * @memberof DisplayObject# - */ - interactive: { - get: function () { - return this._interactive; - }, - set: function (value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage) { - this.stage.dirty = true; - } - } - }, - - /** * Indicates if the sprite is globally visible. * * @member {boolean} - * @readonly * @memberof DisplayObject# + * @readonly */ worldVisible: { get: function () { @@ -601,19 +389,6 @@ }; /** - * Sets the object's stage reference, the stage this object is connected to - * - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -DisplayObject.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } -}; - -/** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * @@ -675,17 +450,17 @@ /** * Internal method. * - * @param renderSession {Object} The render session + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCachedSprite = function (renderSession) { +DisplayObject.prototype._renderCachedSprite = function (renderer) { this._cachedSprite.worldAlpha = this.worldAlpha; - if (renderSession.gl) { - Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + if (renderer.gl) { + Sprite.prototype.renderWebGL.call(this._cachedSprite, renderer); } else { - Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); + Sprite.prototype.renderCanvas.call(this._cachedSprite, renderer); } }; @@ -699,7 +474,7 @@ var bounds = this.getLocalBounds(); if (!this._cachedSprite) { - var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderSession.renderer); + var renderTexture = new RenderTexture(bounds.width | 0, bounds.height | 0); //, renderer); this._cachedSprite = new Sprite(renderTexture); this._cachedSprite.worldTransform = this.worldTransform; @@ -746,19 +521,19 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The renderer * @private */ -DisplayObject.prototype._renderWebGL = function (/* renderSession */) { +DisplayObject.prototype.renderWebGL = function (/* renderer */) { // OVERWRITE; }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The renderer * @private */ -DisplayObject.prototype._renderCanvas = function (/* renderSession */) { +DisplayObject.prototype.renderCanvas = function (/* renderer */) { // OVERWRITE; }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index b81a089..3950ebd 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -106,10 +106,6 @@ this.children.splice(index, 0, child); - if (this.stage) { - child.setStageReference(this.stage); - } - return child; } else { @@ -211,10 +207,6 @@ DisplayObjectContainer.prototype.removeChildAt = function (index) { var child = this.getChildAt(index); - if (this.stage) { - child.removeStageReference(); - } - child.parent = null; this.children.splice(index, 1); @@ -236,13 +228,7 @@ var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; ++i) { - var child = removed[i]; - - if (this.stage) { - child.removeStageReference(); - } - - child.parent = null; + removed[i].parent = null; } return removed; @@ -360,51 +346,17 @@ }; /** - * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to. - * - * @param stage {Stage} the stage that the container will have as its current stage reference - */ -DisplayObjectContainer.prototype.setStageReference = function (stage) { - this.stage = stage; - - if (this._interactive) { - this.stage.dirty = true; - } - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].setStageReference(stage); - } -}; - -/** - * Removes the current stage reference from the container and all of its children. - * - */ -DisplayObjectContainer.prototype.removeStageReference = function () { - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].removeStageReference(); - } - - if (this._interactive) { - this.stage.dirty = true; - } - - this.stage = null; -}; - -/** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -DisplayObjectContainer.prototype._renderWebGL = function (renderSession) { +DisplayObjectContainer.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } @@ -413,37 +365,37 @@ if (this._mask || this._filters) { // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.spriteBatch.flush(); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); + renderer.spriteBatch.stop(); + renderer.maskManager.pushMask(this.mask, renderer); + renderer.spriteBatch.start(); } // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); } else { // simple render children! for(i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } }; @@ -451,28 +403,27 @@ /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {CanvasRenderer} The renderer */ -DisplayObjectContainer.prototype._renderCanvas = function (renderSession) { - if (this.visible === false || this.alpha === 0) { +DisplayObjectContainer.prototype.renderCanvas = function (renderer) { + if (!this.visible || this.alpha <= 0) { return; } if (this._cacheAsBitmap) { - this._renderCachedSprite(renderSession); + this._renderCachedSprite(renderer); return; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/Sprite.js b/src/core/display/Sprite.js index 655de5b..1f9db47 100644 --- a/src/core/display/Sprite.js +++ b/src/core/display/Sprite.js @@ -12,11 +12,8 @@ * * ```js * var sprite = new Sprite.fromImage('assets/image.png'); - * yourStage.addChild(sprite); * ``` * - * then obviously don't forget to add it to the stage you have already created - * * @class Sprite * @extends DisplayObjectContainer * @namespace PIXI @@ -39,8 +36,9 @@ * The texture that the sprite is using * * @member {Texture} + * @private */ - this.texture = texture || Texture.EMPTY; + this._texture = null; /** * The width of the sprite (this is initially set by the texture) @@ -75,19 +73,13 @@ this.blendMode = CONST.blendModes.NORMAL; /** - * The shader that will be used to render the texture to the stage. Set to null to remove a current shader. + * The shader that will be used to render the sprite. Set to null to remove a current shader. * * @member {AbstractFilter} */ this.shader = null; - // wait for the texture to load - if (this.texture.baseTexture.hasLoaded) { - this.onTextureUpdate(); - } - else { - this.texture.on('update', this.onTextureUpdate.bind(this)); - } + this.texture = texture || Texture.EMPTY; this.renderable = true; } @@ -128,25 +120,41 @@ this.scale.y = value / this.texture.frame.height; this._height = value; } - } -}); + }, -/** - * Sets the texture of the sprite - * - * @param texture {Texture} The PIXI texture that is displayed by the sprite - */ -Sprite.prototype.setTexture = function (texture) { - this.texture = texture; - this.cachedTint = 0xFFFFFF; -}; + /** + * The height of the sprite, setting this will actually modify the scale to achieve the value set + * + * @member + * @memberof Sprite# + */ + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + this.cachedTint = 0xFFFFFF; + + if (value) { + // wait for the texture to load + if (value.baseTexture.hasLoaded) { + this._onTextureUpdate(); + } + else { + value.once('update', this._onTextureUpdate.bind(this)); + } + } + } + }, +}); /** * When the texture is updated, this event will fire to update the scale and frame * * @private */ -Sprite.prototype.onTextureUpdate = function () { +Sprite.prototype._onTextureUpdate = function () { // so if _width is 0 then width was not set.. if (this._width) { this.scale.x = this._width / this.texture.frame.width; @@ -258,10 +266,9 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} - * @private + * @param renderer {WebGLRenderer} The renderer */ -Sprite.prototype._renderWebGL = function (renderSession) { +Sprite.prototype.renderWebGL = function (renderer) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0) { return; @@ -271,17 +278,17 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { - var spriteBatch = renderSession.spriteBatch; + var spriteBatch = renderer.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); + renderer.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); + renderer.maskManager.pushMask(this.mask, renderer); spriteBatch.start(); } @@ -290,28 +297,28 @@ // now loop through the children and make sure they get rendered for (i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) { - renderSession.maskManager.popMask(this._mask, renderSession); + renderer.maskManager.popMask(this._mask, renderer); } if (this._filters) { - renderSession.filterManager.popFilter(); + renderer.filterManager.popFilter(); } spriteBatch.start(); } else { - renderSession.spriteBatch.render(this); + renderer.spriteBatch.render(this); // simple render children! for (i = 0, j = this.children.length; i < j; ++i) { - this.children[i]._renderWebGL(renderSession); + this.children[i].renderWebGL(renderer); } } @@ -320,34 +327,32 @@ /** * Renders the object using the Canvas renderer * -* @param renderSession {RenderSession} -* @private +* @param renderer {CanvasRenderer} The renderer */ -Sprite.prototype._renderCanvas = function (renderSession) { - // If the sprite is not visible or the alpha is 0 then no need to render this element +Sprite.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } - if (this.blendMode !== renderSession.currentBlendMode) { - renderSession.currentBlendMode = this.blendMode; - renderSession.context.globalCompositeOperation = blendModesCanvas[renderSession.currentBlendMode]; + if (this.blendMode !== renderer.currentBlendMode) { + renderer.currentBlendMode = this.blendMode; + renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode]; } if (this._mask) { - renderSession.maskManager.pushMask(this._mask, renderSession); + renderer.maskManager.pushMask(this._mask, renderer); } // Ignore null sources if (this.texture.valid) { - var resolution = this.texture.baseTexture.resolution / renderSession.resolution; + var resolution = this.texture.baseTexture.resolution / renderer.resolution; - renderSession.context.globalAlpha = this.worldAlpha; + renderer.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture - if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { - renderSession.scaleMode = this.texture.baseTexture.scaleMode; - renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === CONST.scaleModes.LINEAR); + if (renderer.smoothProperty && renderer.scaleMode !== this.texture.baseTexture.scaleMode) { + renderer.scaleMode = this.texture.baseTexture.scaleMode; + renderer.context[renderer.smoothProperty] = (renderer.scaleMode === CONST.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions @@ -355,27 +360,27 @@ var dy = (this.texture.trim ? this.texture.trim.y : 0) - (this.anchor.y * this.texture.trim.height); // Allow for pixel rounding - if (renderSession.roundPixels) { - renderSession.context.setTransform( + if (renderer.roundPixels) { + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - (this.worldTransform.tx * renderSession.resolution) | 0, - (this.worldTransform.ty * renderSession.resolution) | 0 + (this.worldTransform.tx * renderer.resolution) | 0, + (this.worldTransform.ty * renderer.resolution) | 0 ); dx = dx | 0; dy = dy | 0; } else { - renderSession.context.setTransform( + renderer.context.setTransform( this.worldTransform.a, this.worldTransform.b, this.worldTransform.c, this.worldTransform.d, - this.worldTransform.tx * renderSession.resolution, - this.worldTransform.ty * renderSession.resolution + this.worldTransform.tx * renderer.resolution, + this.worldTransform.ty * renderer.resolution ); } @@ -387,7 +392,7 @@ this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); } - renderSession.context.drawImage( + renderer.context.drawImage( this.tintedTexture, 0, 0, @@ -400,7 +405,7 @@ ); } else { - renderSession.context.drawImage( + renderer.context.drawImage( this.texture.baseTexture.source, this.texture.crop.x, this.texture.crop.y, @@ -415,11 +420,11 @@ } for (var i = 0, j = this.children.length; i < j; i++) { - this.children[i]._renderCanvas(renderSession); + this.children[i].renderCanvas(renderer); } if (this._mask) { - renderSession.maskManager.popMask(renderSession); + renderer.maskManager.popMask(renderer); } }; diff --git a/src/core/display/SpriteBatch.js b/src/core/display/SpriteBatch.js index 59aa55c..959af0e 100644 --- a/src/core/display/SpriteBatch.js +++ b/src/core/display/SpriteBatch.js @@ -5,15 +5,13 @@ * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). - * Any other functionality like interactions, tinting, etc will not work on sprites in this batch. + * Any other functionality like tinting, masking, etc will not work on sprites in this batch. * * It's extremely easy to use : * * ```js * var container = new SpriteBatch(); * - * stage.addChild(container); - * * for(var i = 0; i < 100; ++i) { * var sprite = new PIXI.Sprite.fromImage("myImage.png"); * container.addChild(sprite); @@ -41,9 +39,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatch.prototype.initWebGL = function (gl) { - // TODO only one needed for the whole engine really? - this.fastSpriteBatch = new WebGLFastSpriteBatch(gl); +SpriteBatch.prototype.initWebGL = function (renderer) { + this.fastSpriteBatch = new WebGLFastSpriteBatch(renderer); this.ready = true; }; @@ -62,40 +59,40 @@ /** * Renders the object using the WebGL renderer * - * @param renderSession {RenderSession} + * @param renderer {WebGLRenderer} The webgl renderer * @private */ -SpriteBatch.prototype._renderWebGL = function (renderSession) { +SpriteBatch.prototype.renderWebGL = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } if (!this.ready) { - this.initWebGL(renderSession.gl); + this.initWebGL(renderer); } - renderSession.spriteBatch.stop(); + renderer.spriteBatch.stop(); - renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); + renderer.shaderManager.setShader(renderer.shaderManager.fastShader); - this.fastSpriteBatch.begin(this, renderSession); + this.fastSpriteBatch.begin(this, renderer); this.fastSpriteBatch.render(this); - renderSession.spriteBatch.start(); + renderer.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * - * @param renderSession {RenderSession} + * @param renderer {CanvasRenderer} The canvas renderer * @private */ -SpriteBatch.prototype._renderCanvas = function (renderSession) { +SpriteBatch.prototype.renderCanvas = function (renderer) { if (!this.visible || this.alpha <= 0 || !this.children.length) { return; } - var context = renderSession.context; + var context = renderer.context; var transform = this.worldTransform; var isRotated = true; @@ -150,7 +147,7 @@ var childTransform = child.worldTransform; - if (renderSession.roundPixels) { + if (renderer.roundPixels) { context.setTransform( childTransform.a, childTransform.b, diff --git a/src/core/display/Stage.js b/src/core/display/Stage.js deleted file mode 100644 index 9d365c2..0000000 --- a/src/core/display/Stage.js +++ /dev/null @@ -1,133 +0,0 @@ -var math = require('../math'), - utils = require('../utils'), - DisplayObjectContainer = require('./DisplayObjectContainer'), - InteractionManager = require('../../interaction/InteractionManager'); - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered, but - * the stage itself cannot be transformed. If you want to transform everything within a stage use a single - * DOC as a child of the stage and transform that one. - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this: - * - * ```js - * var stage = new Stage(0xFFFFFF); - * ``` - * - * Where the parameter given is the background colour of the stage. You will use this stage instance to - * add your sprites to it and therefore to the renderer. Here is how to add a sprite to the stage: - * - * ```js - * stage.addChild(sprite); - * ``` - * - * @class - * @extends DisplayObjectContainer - * @namespace PIXI - * @param backgroundColor {number} the background color of the stage, e.g.: 0xFFFFFF for white - */ -function Stage(backgroundColor) { - DisplayObjectContainer.call(this); - - /** - * Current transform of the object based on world (parent) factors - * - * @member {Matrix} - * @readonly - * @private - */ - this.worldTransform = new math.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @member {boolean} - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @member {InteractionManager} - */ - this.interactionManager = new InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @member {boolean} - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new math.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -} - -// constructor -Stage.prototype = Object.create(DisplayObjectContainer.prototype); -Stage.prototype.constructor = Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -Stage.prototype.setInteractionDelegate = function (domElement) { - this.interactionManager.setTargetDomElement(domElement); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -Stage.prototype.updateTransform = function () { - this.worldAlpha = 1; - - for (var i = 0, j = this.children.length; i < j; ++i) { - this.children[i].updateTransform(); - } - - if (this.dirty) { - this.dirty = false; - - // update interactive! - this.interactionManager.dirty = true; - } - - if (this.interactive) { - this.interactionManager.update(); - } -}; - -/** - * Sets the background color for the stage - * - * @param backgroundColor {number} The color of the background, e.g.: 0xFFFFFF for white - */ -Stage.prototype.setBackgroundColor = function (backgroundColor) { - this.backgroundColor = backgroundColor || 0x000000; - this.backgroundColorSplit = utils.hex2rgb(this.backgroundColor); - - var hex = this.backgroundColor.toString(16); - hex = '000000'.substr(0, 6 - hex.length) + hex; - - this.backgroundColorString = '#' + hex; -}; - -/** - * This will return the point containing global coordinates of the mouse. - * - * @return {Point} A point containing the coordinates of the global InteractionData position. - */ -Stage.prototype.getMousePosition = function () { - return this.interactionManager.mouse.global; -}; diff --git a/src/core/index.js b/src/core/index.js index c5eeaaa..ac932ec 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -20,7 +20,6 @@ DisplayObjectContainer: require('./display/DisplayObjectContainer'), Sprite: require('./display/Sprite'), SpriteBatch: require('./display/SpriteBatch'), - Stage: require('./display/Stage'), // textures Texture: require('./textures/Texture'), diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index f36ef72..4eaef9d 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -3,7 +3,7 @@ CONST = require('../../const'); /** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) * * @class @@ -50,8 +50,8 @@ /** * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. + * If the scene is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. + * If the scene is transparent Pixi will use clearRect to clear the canvas every frame. * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. * * @member {boolean} @@ -130,40 +130,39 @@ this.maskManager = new CanvasMaskManager(); /** - * The render session is just a bunch of parameter used for rendering - * @member {object} + * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. + * Handy for crisp pixel art and speed on legacy devices. + * + * @member {boolean} */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; + this.roundPixels = false; - this.mapBlendModes(); + this.scaleMode = null; + + this.smoothProperty = null; + + this.currentBlendMode = CONST.blendModes.NORMAL; + + this.blendModes = null; + + this._mapBlendModes(); this.resize(width, height); if (this.context.imageSmoothingEnabled) { - this.renderSession.smoothProperty = 'imageSmoothingEnabled'; + this.smoothProperty = 'imageSmoothingEnabled'; } else if (this.context.webkitImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'webkitImageSmoothingEnabled'; + this.smoothProperty = 'webkitImageSmoothingEnabled'; } else if (this.context.mozImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'mozImageSmoothingEnabled'; + this.smoothProperty = 'mozImageSmoothingEnabled'; } else if (this.context.oImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'oImageSmoothingEnabled'; + this.smoothProperty = 'oImageSmoothingEnabled'; } else if (this.context.msImageSmoothingEnabled) { - this.renderSession.smoothProperty = 'msImageSmoothingEnabled'; + this.smoothProperty = 'msImageSmoothingEnabled'; } } @@ -172,19 +171,19 @@ module.exports = CanvasRenderer; /** - * Renders the Stage to this canvas view + * Renders the object to this canvas view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (stage) { - stage.updateTransform(); +CanvasRenderer.prototype.render = function (object) { + object.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; - this.renderSession.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModesCanvas[CONST.blendModes.NORMAL]; + this.currentBlendMode = CONST.blendModes.NORMAL; + this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; @@ -196,21 +195,12 @@ this.context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = stage.backgroundColorString; + this.context.fillStyle = object.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(stage); - - // run interaction! - if (stage.interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } + this.renderDisplayObject(object); }; /** @@ -230,7 +220,6 @@ this.view = null; this.context = null; this.maskManager = null; - this.renderSession = null; }; /** @@ -256,13 +245,10 @@ * Renders a display object * * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @private */ -CanvasRenderer.prototype.renderDisplayObject = function (displayObject, context) { - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); +CanvasRenderer.prototype.renderDisplayObject = function (displayObject) { + displayObject.renderCanvas(this); }; /** @@ -270,48 +256,48 @@ * * @private */ -CanvasRenderer.prototype.mapBlendModes = function () { - if (!blendModesCanvas) { - blendModesCanvas = []; +CanvasRenderer.prototype._mapBlendModes = function () { + if (!this.blendModes) { + this.blendModes = {}; if (utils.canUseNewCanvasBlendModes()) { - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'multiply'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'screen'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'overlay'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'darken'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'lighten'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'color-burn'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'hard-light'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'difference'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'exclusion'; - blendModesCanvas[CONST.blendModes.HUE] = 'hue'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'saturation'; - blendModesCanvas[CONST.blendModes.COLOR] = 'color'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'luminosity'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'multiply'; + this.blendModes[CONST.blendModes.SCREEN] = 'screen'; + this.blendModes[CONST.blendModes.OVERLAY] = 'overlay'; + this.blendModes[CONST.blendModes.DARKEN] = 'darken'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'lighten'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'color-dodge'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'color-burn'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'hard-light'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'soft-light'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'difference'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'exclusion'; + this.blendModes[CONST.blendModes.HUE] = 'hue'; + this.blendModes[CONST.blendModes.SATURATION] = 'saturation'; + this.blendModes[CONST.blendModes.COLOR] = 'color'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'luminosity'; } else { // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough' - blendModesCanvas[CONST.blendModes.NORMAL] = 'source-over'; - blendModesCanvas[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? - blendModesCanvas[CONST.blendModes.MULTIPLY] = 'source-over'; - blendModesCanvas[CONST.blendModes.SCREEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.OVERLAY] = 'source-over'; - blendModesCanvas[CONST.blendModes.DARKEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.LIGHTEN] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_DODGE] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR_BURN] = 'source-over'; - blendModesCanvas[CONST.blendModes.HARD_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.SOFT_LIGHT] = 'source-over'; - blendModesCanvas[CONST.blendModes.DIFFERENCE] = 'source-over'; - blendModesCanvas[CONST.blendModes.EXCLUSION] = 'source-over'; - blendModesCanvas[CONST.blendModes.HUE] = 'source-over'; - blendModesCanvas[CONST.blendModes.SATURATION] = 'source-over'; - blendModesCanvas[CONST.blendModes.COLOR] = 'source-over'; - blendModesCanvas[CONST.blendModes.LUMINOSITY] = 'source-over'; + this.blendModes[CONST.blendModes.NORMAL] = 'source-over'; + this.blendModes[CONST.blendModes.ADD] = 'lighter'; //IS THIS OK??? + this.blendModes[CONST.blendModes.MULTIPLY] = 'source-over'; + this.blendModes[CONST.blendModes.SCREEN] = 'source-over'; + this.blendModes[CONST.blendModes.OVERLAY] = 'source-over'; + this.blendModes[CONST.blendModes.DARKEN] = 'source-over'; + this.blendModes[CONST.blendModes.LIGHTEN] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_DODGE] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR_BURN] = 'source-over'; + this.blendModes[CONST.blendModes.HARD_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = 'source-over'; + this.blendModes[CONST.blendModes.DIFFERENCE] = 'source-over'; + this.blendModes[CONST.blendModes.EXCLUSION] = 'source-over'; + this.blendModes[CONST.blendModes.HUE] = 'source-over'; + this.blendModes[CONST.blendModes.SATURATION] = 'source-over'; + this.blendModes[CONST.blendModes.COLOR] = 'source-over'; + this.blendModes[CONST.blendModes.LUMINOSITY] = 'source-over'; } } }; diff --git a/src/core/renderers/canvas/utils/CanvasMaskManager.js b/src/core/renderers/canvas/utils/CanvasMaskManager.js index 6e61b61..61ada5f 100644 --- a/src/core/renderers/canvas/utils/CanvasMaskManager.js +++ b/src/core/renderers/canvas/utils/CanvasMaskManager.js @@ -15,16 +15,16 @@ * This method adds it to the current stack of masks. * * @param maskData {object} the maskData that will be pushed - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.pushMask = function (maskData, renderSession) { - renderSession.context.save(); +CanvasMaskManager.prototype.pushMask = function (maskData, renderer) { + renderer.context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; - var resolution = renderSession.resolution; + var resolution = renderer.resolution; - renderSession.context.setTransform( + renderer.context.setTransform( transform.a * resolution, transform.b * resolution, transform.c * resolution, @@ -33,9 +33,9 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphicsMask(maskData, renderSession.context); + CanvasGraphics.renderGraphicsMask(maskData, renderer.context); - renderSession.context.clip(); + renderer.context.clip(); maskData.worldAlpha = cacheAlpha; }; @@ -43,8 +43,8 @@ /** * Restores the current drawing context to the state it was before the mask was applied. * - * @param renderSession {object} The renderSession whose context will be used for this mask manager. + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer context to use. */ -CanvasMaskManager.prototype.popMask = function (renderSession) { - renderSession.context.restore(); +CanvasMaskManager.prototype.popMask = function (renderer) { + renderer.context.restore(); }; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 78dd426..9913f0a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -12,7 +12,7 @@ instances = []; /** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer + * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) @@ -69,6 +69,24 @@ this.transparent = options.transparent; /** + * The background color as a number. + * + * @member {number} + * @private + */ + this._backgroundColor = 0x000000; + + /** + * The background color as an [R, G, B] array. + * + * @member {number[]} + * @private + */ + this._backgroundColorRgb = [0, 0, 0]; + + this.backgroundColor = options.backgroundColor || this._backgroundColor; // run bg color setter + + /** * Whether the render view should be resized automatically * * @member {boolean} @@ -84,8 +102,8 @@ /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. + * If the renderer is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). + * If the renderer is transparent, Pixi will clear to the target Stage's background color. * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. * * @member {boolean} @@ -153,75 +171,83 @@ */ this.offset = new math.Point(0, 0); + /** + * Counter for the number of draws made each frame + * + * @member {number} + */ + this.drawCount = 0; + // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @member {WebGLShaderManager} */ - this.shaderManager = new WebGLShaderManager(); + this.shaderManager = new WebGLShaderManager(this); /** * Manages the rendering of sprites * @member {WebGLSpriteBatch} */ - this.spriteBatch = new WebGLSpriteBatch(); + this.spriteBatch = new WebGLSpriteBatch(this); /** * Manages the masks using the stencil buffer * @member {WebGLMaskManager} */ - this.maskManager = new WebGLMaskManager(); + this.maskManager = new WebGLMaskManager(this); /** * Manages the filters * @member {WebGLFilterManager} */ - this.filterManager = new WebGLFilterManager(); + this.filterManager = new WebGLFilterManager(this); /** * Manages the stencil buffer * @member {WebGLStencilManager} */ - this.stencilManager = new WebGLStencilManager(); + this.stencilManager = new WebGLStencilManager(this); /** * Manages the blendModes * @member {WebGLBlendModeManager} */ - this.blendModeManager = new WebGLBlendModeManager(); + this.blendModeManager = new WebGLBlendModeManager(this); - /** - * TODO remove - * @member {object} - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; + this.blendModes = null; // time init the context.. - this.initContext(); + this._initContext(); // map some webGL blend modes.. - this.mapBlendModes(); + this._mapBlendModes(); } // constructor WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; +utils.EventTarget.mixin(WebGLRenderer.prototype); + +Object.defineProperties(WebGLRenderer.prototype, { + backgroundColor: { + get: function () { + return this._backgroundColor; + }, + set: function (val) { + this._backgroundColor = val; + utils.hex2rgb(val, this._backgroundColorRgb); + } + } +}); + /** -* @method initContext -*/ -WebGLRenderer.prototype.initContext = function () { + * + * @private + */ +WebGLRenderer.prototype._initContext = function () { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; @@ -241,62 +267,28 @@ gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; + this.emit('context', gl); // now resize and we are good to go! this.resize(this.width, this.height); }; /** - * Renders the stage to its webGL view + * Renders the object to its webGL view * - * @param stage {Stage} the Stage element to be rendered + * @param object {DisplayObject} the object to be rendered */ -WebGLRenderer.prototype.render = function (stage) { +WebGLRenderer.prototype.render = function (object) { // no point rendering if our context has been blown up! if (this.contextLost) { return; } - // if rendering a new stage clear the batches.. - if (this.__stage !== stage) { - if (stage.interactive) { - stage.interactionManager.removeEvents(); - } - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - // update the scene graph - stage.updateTransform(); + object.updateTransform(); var gl = this.gl; - // interaction - if (stage._interactive) { - //need to add some events! - if (!stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else { - if (stage._interactiveEventsAdded) { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); @@ -308,13 +300,13 @@ gl.clearColor(0, 0, 0, 0); } else { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + gl.clearColor(object.backgroundColorSplit[0], object.backgroundColorSplit[1], object.backgroundColorSplit[2], 1); } - gl.clear (gl.COLOR_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT); } - this.renderDisplayObject( stage, this.projection ); + this.renderDisplayObject(object, this.projection); }; /** @@ -325,28 +317,28 @@ * @param buffer {Array} a standard WebGL buffer */ WebGLRenderer.prototype.renderDisplayObject = function (displayObject, projection, buffer) { - this.renderSession.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); + this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); // reset the render session data.. - this.renderSession.drawCount = 0; + this.drawCount = 0; // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; + this.flipY = buffer ? -1 : 1; // set the default projection - this.renderSession.projection = projection; + this.projection = projection; //set the default offset - this.renderSession.offset = this.offset; + this.offset = this.offset; // start the sprite batch - this.spriteBatch.begin(this.renderSession); + this.spriteBatch.begin(this); // start the filter manager - this.filterManager.begin(this.renderSession, buffer); + this.filterManager.begin(this, buffer); // render the scene! - displayObject._renderWebGL(this.renderSession); + displayObject.renderWebGL(this); // finish the sprite batch this.spriteBatch.end(); @@ -441,7 +433,7 @@ * @private */ WebGLRenderer.prototype.handleContextRestored = function () { - this.initContext(); + this._initContext(); // empty all the ol gl textures as they are useless now for (var key in utils.TextureCache) { @@ -478,36 +470,36 @@ this.filterManager = null; this.gl = null; - this.renderSession = null; }; /** * Maps Pixi blend modes to WebGL blend modes. * + * @private */ WebGLRenderer.prototype.mapBlendModes = function () { var gl = this.gl; - if (!blendModesWebGL) { - blendModesWebGL = []; + if (!this.blendModes) { + this.blendModes = {}; - blendModesWebGL[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - blendModesWebGL[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - blendModesWebGL[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - blendModesWebGL[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + this.blendModes[CONST.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + this.blendModes[CONST.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + this.blendModes[CONST.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; } }; diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js index c4c0001..70652d1 100644 --- a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,106 +6,51 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function ComplexPrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + 'uniform float flipY;', + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'precision mediump float;', + 'varying vec4 vColor;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; module.exports = ComplexPrimitiveShader; - -/** - * Initialises the shader. - * - */ -ComplexPrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -ComplexPrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiFastShader.js b/src/core/renderers/webgl/shaders/PixiFastShader.js index 58309cf..9fad949 100644 --- a/src/core/renderers/webgl/shaders/PixiFastShader.js +++ b/src/core/renderers/webgl/shaders/PixiFastShader.js @@ -1,143 +1,59 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class + * @extends Shader * @namespace PIXI * @param gl {WebGLContext} the current WebGL drawing context */ function PixiFastShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + null, + // fragment shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aPositionCoord;', + 'attribute vec2 aScale;', + 'attribute float aRotation;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform mat3 uMatrix;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', + 'varying float vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; + 'const vec2 center = vec2(-1.0, 1.0);', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - this.init(); + 'void main(void) {', + ' vec2 v;', + ' vec2 sv = aVertexPosition * aScale;', + ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', + ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', + ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', + ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = aColor;', + '}' + ].join('\n'), + // custom uniforms + { + uMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aPositionCoord: 0, + aRotation: 0, + aScale: 0 + } + ); } +PixiFastShader.prototype = Object.create(Shader.prototype); PixiFastShader.prototype.constructor = PixiFastShader; module.exports = PixiFastShader; - -/** - * Initialises the shader. - * - */ -PixiFastShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PixiFastShader.prototype.destroy = function () { - this.gl.deleteProgram(this.program); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/PixiShader.js b/src/core/renderers/webgl/shaders/PixiShader.js deleted file mode 100644 index 2d67a4f..0000000 --- a/src/core/renderers/webgl/shaders/PixiShader.js +++ /dev/null @@ -1,336 +0,0 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); - -/** - * @class - * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context - */ -function PixiShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); - - /** - * @member {WebGLContext} - */ - this.gl = gl; - - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; - - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @member {number} - */ - this.textureCount = 0; - - /** - * A local flag - * @member {boolean} - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @member {boolean} - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @member {Array} - * @private - */ - this.attributes = []; - - this.init(); -} - -PixiShader.prototype.constructor = PixiShader; -module.exports = PixiShader; - -/** - * Initialises the shader. - * - */ -PixiShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc || PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if (this.colorAttribute === -1) { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** - * Initialises the shader uniform values. - * - * Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ - * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf - * - */ -PixiShader.prototype.initUniforms = function () { - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') { - uniform._init = false; - - if (uniform.value !== null) { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') { - uniform.glValueLength = 4; - } - else { - uniform.glValueLength = 1; - } - } - } - -}; - -/** - * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) - * - */ -PixiShader.prototype.initSampler2D = function (uniform) { - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; -}; - -/** - * Updates the shader uniform values. - * - */ -PixiShader.prototype.syncUniforms = function () { - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) { - if (uniform.glMatrix === true) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') { - if (uniform._init) { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if (uniform.value.baseTexture._dirty[gl.id]) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else { - this.initSampler2D(uniform); - } - } - } -}; - -/** - * Destroys the shader. - * - */ -PixiShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * The Default Vertex shader source. - * - * @property defaultVertexSrc - * @type String - */ -PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js index 2f5891e..28714f5 100644 --- a/src/core/renderers/webgl/shaders/PrimitiveShader.js +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,101 +6,49 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function PrimitiveShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + // 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec4 vColor;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + 'varying vec4 vColor;', - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + flipY: { type: '1f', value: 0 }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +PrimitiveShader.prototype = Object.create(Shader.prototype); PrimitiveShader.prototype.constructor = PrimitiveShader; module.exports = PrimitiveShader; - -/** - * Initialises the shader. - * - */ -PrimitiveShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -PrimitiveShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js new file mode 100644 index 0000000..60e97be --- /dev/null +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -0,0 +1,453 @@ +var utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param [vertexSrc] {string} The source of the vertex shader. + */ +function Shader(gl, fragmentSrc, vertexSrc, customUniforms, customAttributes) { + /** + * @member {number} + * @readonly + */ + this.uuid = utils.uuid(); + + /** + * @member {WebGLContext} + * @readonly + */ + this.gl = gl; + + /** + * The WebGL program. + * @member {WebGLProgram} + * @readonly + */ + this.program = null; + + this.uniforms = { + uSampler: { type: 'sampler2D', value: 0 }, + projectionVector: { type: '2f', value: { x: 0, y: 0 } }, + offsetVector: { type: '2f', value: { x: 0, y: 0 } }, + dimensions: { type: '4f', value: new Float32Array(4) } + }; + + for (var u in customUniforms) { + this.uniforms[u] = customUniforms[u]; + } + + this.attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + for (var a in customAttributes) { + this.attributes[a] = customAttributes[a]; + } + + this.textureCount = 0; + + /** + * The vertex shader. + * @member {Array} + */ + this.vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + this.fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + this.init(); +} + +Shader.prototype.constructor = Shader; +module.exports = Shader; + +Shader.prototype.init = function () { + this.compile(); + + this.gl.useProgram(this.program); + + this.cacheUniformLocations(this.builtInUniforms.concat(Object.keys(this.uniforms))); + this.cacheAttributeLocations(this.builtInAttributes.concat(Object.keys(this.attributes))); +}; + +Shader.prototype.cacheUniformLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.uniforms[keys[i]]._location = this.gl.getUniformLocation(this.program, keys[i]); + } +}; + +Shader.prototype.cacheAttributeLocations = function (keys) { + for (var i = 0; i < keys.length; ++i) { + this.attributes[keys[i]] = this.gl.getAttributeLocation(this.program, keys[i]); + } + + // TODO: Check if this is needed anymore... + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its something to do with the current state of the gl context. + // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if (this._shader.attributes.aColor === -1) { + this._shader.attributes.aColor = 2; + } + + // End worst hack eva // +}; + +Shader.prototype.compile = function () { + var gl = this.gl; + + var glVertShader = this._glCompile(gl.VERTEX_SHADER, this.vertexSrc); + var glFragShader = this._glCompile(gl.FRAGMENT_SHADER, this.fragmentSrc); + + var program = gl.createProgram(); + + gl.attachShader(program, glVertShader); + gl.attachShader(program, glFragShader); + gl.linkProgram(program); + + // if linking fails, then log and cleanup + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + window.console.error('Pixi.js Error: Could not initialize shader.'); + window.console.error('gl.VALIDATE_STATUS', gl.getProgramParameter(program, gl.VALIDATE_STATUS)); + window.console.error('gl.getError()', gl.getError()); + + gl.deleteProgram(program); + program = null; + } + + // if there is a program info log, log it + if (gl.getProgramInfoLog(program) !== '') { + window.console.warn('Pixi.js Warning: gl.getProgramInfoLog()', gl.getProgramInfoLog(program)); + } + + // clean up some shaders + gl.deleteShader(glVertShader); + gl.deleteShader(glFragShader); + + return (this.program = program); +}; + +Shader.prototype.syncUniforms = function () { + var gl = this.gl; + + this.textureCount = 1; + + for (var key in this.uniforms) { + var uniform = this.uniforms[key], + location = uniform._location, + value = uniform.value, + i, il; + + switch (uniform.type) { + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + case '1iv': + gl.uniform1iv(location, value); + break; + + case '3iv': + gl.uniform3iv(location, value); + break; + + case '1fv': + gl.uniform1fv(location, value); + break; + + case '2fv': + gl.uniform2fv(location, value); + break; + + case '3fv': + gl.uniform3fv(location, value); + break; + + case '4fv': + gl.uniform4fv(location, value); + break; + + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + case 'm3': + case 'mat3': + case 'Matrix3fv': + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) { + this.initSampler2D(uniform); + + uniform._init = true; + } + // if it has been initialized, check if dirty and needs update + else if (uniform.value.baseTexture._dirty[gl.id] !== false) { + instances[gl.id].updateTexture(uniform.value.baseTexture); + } + + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } + } +}; + + +/** + * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) + * + */ +Shader.prototype.initSampler2D = function (uniform) { + var gl = this.gl; + + // Extended texture data + if (uniform.textureData) { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); + + if (data.width) { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } +}; + +/** + * Destroys the shader. + * + */ +Shader.prototype.destroy = function () { + this.gl.deleteProgram(this.program); + + this.gl = null; + this.uniforms = null; + this.attributes = null; + + this.vertexSrc = null; + this.fragmentSrc = null; +}; + +Shader.prototype._glCompile = function (type, src) { + var shader = this.gl.createShader(type); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + window.console.log(this.gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; diff --git a/src/core/renderers/webgl/shaders/StripShader.js b/src/core/renderers/webgl/shaders/StripShader.js index ee91348..660e932 100644 --- a/src/core/renderers/webgl/shaders/StripShader.js +++ b/src/core/renderers/webgl/shaders/StripShader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - glUtils = require('../utils/WebGLShaderUtils'); +var Shader = require('./Shader'); /** * @class @@ -7,107 +6,47 @@ * @param gl {WebGLContext} the current WebGL drawing context */ function StripShader(gl) { - /** - * @member {number} - * @private - */ - this._UID = utils.uuid(); + Shader.call(this, + gl, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', - /** - * @member {WebGLContext} - */ - this.gl = gl; + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', - /** - * The WebGL program. - * @member {Any} - */ - this.program = null; + 'varying vec2 vTextureCoord;', - /** - * The fragment shader. - * @member {Array} - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; + 'uniform float alpha;', + 'uniform sampler2D uSampler;', - /** - * The vertex shader. - * @member {Array} - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', + 'varying vec2 vTextureCoord;', - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', + '}' + ].join('\n'), + // custom uniforms + { + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) } + } + ); } +StripShader.prototype = Object.create(Shader.prototype); StripShader.prototype.constructor = StripShader; module.exports = StripShader; - -/** - * Initialises the shader. - * - */ -StripShader.prototype.init = function () { - var gl = this.gl; - - var program = glUtils.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** - * Destroys the shader. - * - */ -StripShader.prototype.destroy = function () { - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js index 5085e46..9f2f89a 100644 --- a/src/core/renderers/webgl/utils/WebGLBlendModeManager.js +++ b/src/core/renderers/webgl/utils/WebGLBlendModeManager.js @@ -1,28 +1,24 @@ +var WebGLManager = require('./WebGLManager'); + /** * @class * @namespace PIXI - * @param gl {WebGLContext} the current WebGL drawing context + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLBlendModeManager() { +function WebGLBlendModeManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.currentBlendMode = 99999; } +WebGLBlendModeManager.prototype = Object.create(WebGLManager.prototype); WebGLBlendModeManager.prototype.constructor = WebGLBlendModeManager; module.exports = WebGLBlendModeManager; /** - * Sets the WebGL Context. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLBlendModeManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Sets-up the given blendMode from WebGL's point of view. * * @param blendMode {number} the blendMode, should be a Pixi const, such as BlendModes.ADD @@ -34,16 +30,8 @@ this.currentBlendMode = blendMode; - var blendModeWebGL = blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + var mode = this.renderer.blendModes[this.currentBlendMode]; + this.renderer.gl.blendFunc(mode[0], mode[1]); return true; }; - -/** - * Destroys this object. - * - */ -WebGLBlendModeManager.prototype.destroy = function () { - this.gl = null; -}; diff --git a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js index f18b88b..00642b1 100644 --- a/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -12,7 +12,7 @@ * @class * @namespace PIXI */ -function WebGLFastSpriteBatch(gl) { +function WebGLFastSpriteBatch(renderer) { /** * @member {number} */ @@ -93,11 +93,6 @@ /** * @member {object} */ - this.renderSession = null; - - /** - * @member {object} - */ this.shader = null; /** @@ -105,7 +100,9 @@ */ this.matrix = null; - this.setContext(gl); + this.renderer = renderer; + + this.setupContext(); } WebGLFastSpriteBatch.prototype.constructor = WebGLFastSpriteBatch; @@ -116,8 +113,8 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -WebGLFastSpriteBatch.prototype.setContext = function (gl) { - this.gl = gl; +WebGLFastSpriteBatch.prototype.setupContext = function () { + var gl = this.renderer.gl; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -135,11 +132,11 @@ /** * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {object} + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer */ -WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderSession) { - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; +WebGLFastSpriteBatch.prototype.begin = function (spriteBatch, renderer) { + this.renderer = renderer; + this.shader = renderer.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); @@ -169,9 +166,9 @@ this.currentBaseTexture = sprite.texture.baseTexture; // check blend mode - if (sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) { + if (sprite.blendMode !== this.renderer.blendModeManager.currentBlendMode) { this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); + this.renderer.blendModeManager.setBlendMode(sprite.blendMode); } for (var i=0,j= children.length; i 1) { gl.viewport(0, 0, filterArea.width, filterArea.height); @@ -177,7 +183,7 @@ var inputTexture = texture; var outputTexture = this.texturePool.pop(); if (!outputTexture) { - outputTexture = new FilterTexture(this.gl, this.width, this.height); + outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); } outputTexture.resize(this.width, this.height); @@ -297,7 +303,7 @@ this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); + // this.renderer.shaderManager.setShader(this.defaultShader); // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); @@ -317,7 +323,8 @@ */ WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) { // use program - var gl = this.gl; + var gl = this.renderer.gl; + var shader = filter.shaders[gl.id]; if (!shader) { @@ -331,7 +338,7 @@ } // set the shader - this.renderSession.shaderManager.setShader(shader); + this.renderer.shaderManager.setShader(shader); // gl.useProgram(shader.program); @@ -361,7 +368,7 @@ // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - this.renderSession.drawCount++; + this.renderer.drawCount++; }; /** @@ -369,7 +376,7 @@ * */ WebGLFilterManager.prototype.initShaderBuffers = function () { - var gl = this.gl; + var gl = this.renderer.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); @@ -415,7 +422,7 @@ * */ WebGLFilterManager.prototype.destroy = function () { - var gl = this.gl; + var gl = this.renderer.gl; this.filterStack = null; @@ -434,4 +441,6 @@ gl.deleteBuffer(this.uvBuffer); gl.deleteBuffer(this.colorBuffer); gl.deleteBuffer(this.indexBuffer); + + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLGraphics.js b/src/core/renderers/webgl/utils/WebGLGraphics.js index 86efe8f..09f540f 100644 --- a/src/core/renderers/webgl/utils/WebGLGraphics.js +++ b/src/core/renderers/webgl/utils/WebGLGraphics.js @@ -17,13 +17,14 @@ * @static * @private * @param graphics {Graphics} - * @param renderSession {object} + * @param renderer {WebGLRenderer} */ -WebGLGraphics.renderGraphics = function (graphics, renderSession) {//projection, offset) { - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader, +WebGLGraphics.renderGraphics = function (graphics, renderer) {//projection, offset) { + var gl = renderer.gl; + + var projection = renderer.projection, + offset = renderer.offset, + shader = renderer.shaderManager.primitiveShader, webGLData; if (graphics.dirty) { @@ -38,19 +39,19 @@ if (webGL.data[i].mode === 1) { webGLData = webGL.data[i]; - renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); + renderer.stencilManager.pushStencil(graphics, webGLData, renderer); // render quad.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); + renderer.stencilManager.popStencil(graphics, webGLData, renderer); } else { webGLData = webGL.data[i]; - renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); + shader = renderer.shaderManager.primitiveShader; gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, 1); diff --git a/src/core/renderers/webgl/utils/WebGLManager.js b/src/core/renderers/webgl/utils/WebGLManager.js new file mode 100644 index 0000000..9d47b5c --- /dev/null +++ b/src/core/renderers/webgl/utils/WebGLManager.js @@ -0,0 +1,20 @@ +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLManager(renderer) { + /** + * The renderer this manager works for. + * + * @member {WebGLRenderer} + */ + this.renderer = renderer; +} + +WebGLManager.prototype.constructor = WebGLManager; +module.exports = WebGLManager; + +WebGLManager.prototype.destroy = function () { + this.renderer = null; +}; diff --git a/src/core/renderers/webgl/utils/WebGLMaskManager.js b/src/core/renderers/webgl/utils/WebGLMaskManager.js index 002c54d..6d46d85 100644 --- a/src/core/renderers/webgl/utils/WebGLMaskManager.js +++ b/src/core/renderers/webgl/utils/WebGLMaskManager.js @@ -1,59 +1,41 @@ -var WebGLGraphics = require('./WebGLGraphics'); +var WebGLManager = require('./WebGLManager'), + WebGLGraphics = require('./WebGLGraphics'); /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLMaskManager() {} +function WebGLMaskManager(renderer) { + WebGLManager.call(this, renderer); +} +WebGLMaskManager.prototype = Object.create(WebGLManager.prototype); WebGLMaskManager.prototype.constructor = WebGLMaskManager; module.exports = WebGLMaskManager; /** - * Sets the drawing context to the one given in parameter. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLMaskManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Applies the Mask and adds it to the current filter stack. * - * @param maskData {Array} - * @param renderSession {object} + * @param maskData {any[]} */ -WebGLMaskManager.prototype.pushMask = function (maskData, renderSession) { - var gl = renderSession.gl; - +WebGLMaskManager.prototype.pushMask = function (maskData) { if (maskData.dirty) { - WebGLGraphics.updateGraphics(maskData, gl); + WebGLGraphics.updateGraphics(maskData, this.renderer.gl); } - if (!maskData._webGL[gl.id].data.length) { + if (!maskData._webGL[this.renderer.gl.id].data.length) { return; } - renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); + this.renderer.stencilManager.pushStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; /** * Removes the last filter from the filter stack and doesn't return it. * - * @param maskData {Array} - * @param renderSession {object} an object containing all the useful parameters + * @param maskData {any[]} */ -WebGLMaskManager.prototype.popMask = function (maskData, renderSession) { - var gl = this.gl; - renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); -}; - -/** - * Destroys the mask stack. - * - */ -WebGLMaskManager.prototype.destroy = function () { - this.gl = null; +WebGLMaskManager.prototype.popMask = function (maskData) { + this.renderer.stencilManager.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderManager.js b/src/core/renderers/webgl/utils/WebGLShaderManager.js index 7f2c443..d02d95d 100644 --- a/src/core/renderers/webgl/utils/WebGLShaderManager.js +++ b/src/core/renderers/webgl/utils/WebGLShaderManager.js @@ -1,4 +1,5 @@ -var PrimitiveShader = require('../shaders/PrimitiveShader'), +var WebGLManager = require('./WebGLManager'), + PrimitiveShader = require('../shaders/PrimitiveShader'), ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), PixiShader = require('../shaders/PixiShader'), PixiFastShader = require('../shaders/PixiFastShader'), @@ -7,21 +8,23 @@ /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLShaderManager() { +function WebGLShaderManager(renderer) { + WebGLManager.call(this, renderer); + /** * @member {number} */ this.maxAttibs = 10; /** - * @member {Array} + * @member {any[]} */ this.attribState = []; /** - * @member {Array} + * @member {any[]} */ this.tempAttribState = []; @@ -30,38 +33,62 @@ } /** - * @member {Array} + * @member {any[]} */ this.stack = []; -} -WebGLShaderManager.prototype.constructor = WebGLShaderManager; -module.exports = WebGLShaderManager; + /** + * @member {number} + * @private + */ + this._currentId = -1; -/** - * Initialises the context and the properties. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLShaderManager.prototype.setContext = function (gl) { - this.gl = gl; + /** + * @member {Shader} + * @private + */ + this.currentShader = null; - // the next one is used for rendering primitives - this.primitiveShader = new PrimitiveShader(gl); + // this shader is used for rendering primitives + this.primitiveShader = null; - // the next one is used for rendering triangle strips - this.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + // this shader is used for rendering triangle strips + this.complexPrimitiveShader = null; // this shader is used for the default sprite rendering - this.defaultShader = new PixiShader(gl); + this.defaultShader = null; // this shader is used for the fast sprite rendering - this.fastShader = new PixiFastShader(gl); + this.fastShader = null; // the next one is used for rendering triangle strips - this.stripShader = new StripShader(gl); - this.setShader(this.defaultShader); -}; + this.stripShader = null; + + // listen for context and update necessary shaders + var self = this; + this.renderer.on('context', function (gl) { + // this shader is used for rendering primitives + self.primitiveShader = new PrimitiveShader(gl); + + // this shader is used for rendering triangle strips + self.complexPrimitiveShader = new ComplexPrimitiveShader(gl); + + // this shader is used for the default sprite rendering + self.defaultShader = new PixiShader(gl); + + // this shader is used for the fast sprite rendering + self.fastShader = new PixiFastShader(gl); + + // the next one is used for rendering triangle strips + self.stripShader = new StripShader(gl); + + self.setShader(self.defaultShader); + }); +} + +WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); +WebGLShaderManager.prototype.constructor = WebGLShaderManager; +module.exports = WebGLShaderManager; /** * Takes the attributes given in parameters. @@ -77,18 +104,17 @@ } // set the new attribs - for (i = 0; i < attribs.length; i++) { - var attribId = attribs[i]; - this.tempAttribState[attribId] = true; + for (var a in attribs) { + this.tempAttribState[attribs[a]] = true; } - var gl = this.gl; + var gl = this.renderer.gl; for (i = 0; i < this.attribState.length; i++) { if (this.attribState[i] !== this.tempAttribState[i]) { this.attribState[i] = this.tempAttribState[i]; - if (this.tempAttribState[i]) { + if (this.attribState[i]) { gl.enableVertexAttribArray(i); } else { @@ -104,15 +130,15 @@ * @param shader {Any} */ WebGLShaderManager.prototype.setShader = function (shader) { - if (this._currentId === shader._UID) { + if (this._currentId === shader.uuid) { return false; } - this._currentId = shader._UID; + this._currentId = shader.uuid; this.currentShader = shader; - this.gl.useProgram(shader.program); + this.renderer.gl.useProgram(shader.program); this.setAttribs(shader.attributes); return true; @@ -128,14 +154,19 @@ this.tempAttribState = null; this.primitiveShader.destroy(); + this.primitiveShader = null; this.complexPrimitiveShader.destroy(); + this.complexPrimitiveShader = null; this.defaultShader.destroy(); + this.defaultShader = null; this.fastShader.destroy(); + this.fastShader = null; this.stripShader.destroy(); + this.stripShader = null; - this.gl = null; + this.renderer = null; }; diff --git a/src/core/renderers/webgl/utils/WebGLShaderUtils.js b/src/core/renderers/webgl/utils/WebGLShaderUtils.js deleted file mode 100644 index 2942e33..0000000 --- a/src/core/renderers/webgl/utils/WebGLShaderUtils.js +++ /dev/null @@ -1,74 +0,0 @@ -var glUtils = module.exports = { - /** - * @static - * @private - */ - initDefaultShaders: function () { - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @return {Any} - */ - CompileVertexShader: function (gl, shaderSrc) { - return glUtils._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @return {Any} - */ - CompileFragmentShader: function (gl, shaderSrc) { - return glUtils._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); - }, - - /** - * @static - * @private - * @param gl {WebGLContext} the current WebGL drawing context - * @param shaderSrc {Array} - * @param shaderType {number} - * @return {Any} - */ - _CompileShader: function (gl, shaderSrc, shaderType) { - var src = shaderSrc.join('\n'); - var shader = gl.createShader(shaderType); - gl.shaderSource(shader, src); - gl.compileShader(shader); - - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - window.console.log(gl.getShaderInfoLog(shader)); - return null; - } - - return shader; - }, - - /** - * @static - * @param gl {WebGLContext} the current WebGL drawing context - * @param vertexSrc {Array} - * @param fragmentSrc {Array} - * @return {Any} - */ - compileProgram: function (gl, vertexSrc, fragmentSrc) { - var fragmentShader = glUtils.CompileFragmentShader(gl, fragmentSrc); - var vertexShader = glUtils.CompileVertexShader(gl, vertexSrc); - - var shaderProgram = gl.createProgram(); - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - window.console.log('Could not initialise shaders'); - } - - return shaderProgram; - } -}; diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index 07f99c3..e0984c4 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -481,7 +481,7 @@ nextShader = sprite.shader || this.defaultShader; blendSwap = currentBlendMode !== nextBlendMode; - shaderSwap = currentShader !== nextShader; // should I use _UIDS??? + shaderSwap = currentShader !== nextShader; // should I use uuidS??? if (currentBaseTexture !== nextTexture || blendSwap || shaderSwap) { this.renderBatch(currentBaseTexture, batchSize, start); diff --git a/src/core/renderers/webgl/utils/WebGLStencilManager.js b/src/core/renderers/webgl/utils/WebGLStencilManager.js index ddba5a1..0bedae6 100644 --- a/src/core/renderers/webgl/utils/WebGLStencilManager.js +++ b/src/core/renderers/webgl/utils/WebGLStencilManager.js @@ -1,38 +1,33 @@ -var utils = require('../../../utils'); +var WebGLManager = require('./WebGLManager'), + utils = require('../../../utils'); /** * @class * @namespace PIXI - * @private + * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLStencilManager() { +function WebGLStencilManager(renderer) { + WebGLManager.call(this, renderer); + this.stencilStack = []; this.reverse = true; this.count = 0; } +WebGLStencilManager.prototype = Object.create(WebGLManager.prototype); WebGLStencilManager.prototype.constructor = WebGLStencilManager; module.exports = WebGLStencilManager; /** - * Sets the drawing context to the one given in parameter. - * - * @param gl {WebGLContext} the current WebGL drawing context - */ -WebGLStencilManager.prototype.setContext = function (gl) { - this.gl = gl; -}; - -/** * Applies the Mask and adds it to the current filter stack. * * @param graphics {Graphics} - * @param webGLData {Array} - * @param renderSession {object} + * @param webGLData {any[]} */ -WebGLStencilManager.prototype.pushStencil = function (graphics, webGLData, renderSession) { - var gl = this.gl; - this.bindGraphics(graphics, webGLData, renderSession); +WebGLStencilManager.prototype.pushStencil = function (graphics, webGLData) { + var gl = this.renderer.gl; + + this.bindGraphics(graphics, webGLData, this.renderer); if (this.stencilStack.length === 0) { gl.enable(gl.STENCIL_TEST); @@ -107,25 +102,24 @@ * * @param graphics {Graphics} * @param webGLData {Array} - * @param renderSession {object} */ -WebGLStencilManager.prototype.bindGraphics = function (graphics, webGLData, renderSession) { +WebGLStencilManager.prototype.bindGraphics = function (graphics, webGLData) { //if (this._currentGraphics === graphics)return; this._currentGraphics = graphics; - var gl = this.gl; + var gl = this.renderer.gl; // bind the graphics object.. - var projection = renderSession.projection, - offset = renderSession.offset, - shader;// = renderSession.shaderManager.primitiveShader; + var projection = this.renderer.projection, + offset = this.renderer.offset, + shader;// = this.renderer.shaderManager.primitiveShader; if (webGLData.mode === 1) { - shader = renderSession.shaderManager.complexPrimitiveShader; + shader = this.renderer.shaderManager.complexPrimitiveShader; - renderSession.shaderManager.setShader( shader ); + this.renderer.shaderManager.setShader(shader); - gl.uniform1f(shader.flipY, renderSession.flipY); + gl.uniform1f(shader.flipY, this.renderer.flipY); gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); @@ -147,13 +141,13 @@ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); } else { - //renderSession.shaderManager.activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; - renderSession.shaderManager.setShader( shader ); + //this.renderer.shaderManager.activatePrimitiveShader(); + shader = this.renderer.shaderManager.primitiveShader; + this.renderer.shaderManager.setShader( shader ); gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); - gl.uniform1f(shader.flipY, renderSession.flipY); + gl.uniform1f(shader.flipY, this.renderer.flipY); gl.uniform2f(shader.projectionVector, projection.x, -projection.y); gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); @@ -174,10 +168,10 @@ /** * @param graphics {Graphics} * @param webGLData {Array} - * @param renderSession {object} */ -WebGLStencilManager.prototype.popStencil = function (graphics, webGLData, renderSession) { - var gl = this.gl; +WebGLStencilManager.prototype.popStencil = function (graphics, webGLData) { + var gl = this.renderer.gl; + this.stencilStack.pop(); this.count--; @@ -191,7 +185,7 @@ var level = this.count; - this.bindGraphics(graphics, webGLData, renderSession); + this.bindGraphics(graphics, webGLData, this.renderer); gl.colorMask(false, false, false, false); @@ -257,6 +251,6 @@ * */ WebGLStencilManager.prototype.destroy = function () { + this.renderer = null; this.stencilStack = null; - this.gl = null; }; diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 26565cd..121a583 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -57,7 +57,8 @@ */ this.source = source; - this._UID = utils.uuid(); + // ID for the base texture + this.uuid = utils.uuid(); /** * Controls if RGB channels should be pre-multiplied by Alpha (WebGL only) @@ -88,10 +89,10 @@ // TODO - this needs to be addressed /** - * @member {Array} + * @member {object} * @private */ - this._dirty = [true, true, true, true]; + this._dirty = {}; if (!source) { return; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 2148338..0ff62e1 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -21,8 +21,14 @@ * @param hex {number} * @return {number[]} An array representing the [R, G, B] of the color. */ - hex2rgb: function (hex) { - return [(hex >> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; + hex2rgb: function (hex, out) { + out = out || []; + + out[0] = (hex >> 16 & 0xFF) / 255; + out[1] = (hex >> 8 & 0xFF) / 255; + out[2] = (hex & 0xFF) / 255; + + return out; }, /**