From 9737710200be158bdc01fc143c919a3bd03c650d Mon Sep 17 00:00:00 2001 From: photonstorm Date: Thu, 6 Feb 2014 00:19:46 +0000 Subject: [PATCH] Upgraded to Pixi.js 1.4.4 --- README.md | 13 +- src/pixi/InteractionData.js | 63 + src/pixi/InteractionManager.js | 276 ++--- src/pixi/Pixi.js | 38 + src/pixi/core/Circle.js | 14 +- src/pixi/core/Ellipse.js | 20 +- src/pixi/core/Matrix.js | 327 +----- src/pixi/core/Point.js | 10 +- src/pixi/core/Polygon.js | 8 +- src/pixi/core/Rectangle.js | 9 +- src/pixi/display/DisplayObject.js | 420 +++---- src/pixi/display/DisplayObjectContainer.js | 428 +++---- src/pixi/display/MovieClip.js | 2 +- src/pixi/display/Sprite.js | 325 +++++- src/pixi/display/SpriteBatch copy.js | 371 ++++++ src/pixi/display/SpriteBatch.js | 156 +++ src/pixi/display/Stage.js | 21 +- src/pixi/extras/CustomRenderable.js | 58 - src/pixi/extras/Rope.js | 30 +- src/pixi/extras/Strip.js | 25 + src/pixi/extras/TilingSprite.js | 342 +++++- src/pixi/filters/AbstractFilter.js | 21 +- src/pixi/filters/AlphaMaskFilter.js | 93 ++ src/pixi/filters/BlurFilter.js | 4 +- src/pixi/filters/BlurXFilter.js | 2 +- src/pixi/filters/BlurYFilter.js | 2 +- src/pixi/filters/ColorMatrixFilter.js | 4 +- src/pixi/filters/ColorStepFilter.js | 6 +- src/pixi/filters/CrossHatchFilter.js | 2 +- src/pixi/filters/DisplacementFilter.js | 5 +- src/pixi/filters/DotScreenFilter.js | 6 +- src/pixi/filters/GrayFilter.js | 4 +- src/pixi/filters/InvertFilter.js | 4 +- src/pixi/filters/NormalMapFilter.js | 225 ++++ src/pixi/filters/PixelateFilter.js | 4 +- src/pixi/filters/RGBSplitFilter.js | 2 +- src/pixi/filters/SepiaFilter.js | 4 +- src/pixi/filters/TwistFilter.js | 6 +- src/pixi/loaders/AssetLoader.js | 7 +- src/pixi/loaders/AtlasLoader.js | 16 +- src/pixi/loaders/BitmapFontLoader.js | 56 +- src/pixi/loaders/ImageLoader.js | 4 +- src/pixi/loaders/JsonLoader.js | 24 +- src/pixi/loaders/SpineLoader.js | 19 +- src/pixi/loaders/SpriteSheetLoader.js | 48 +- src/pixi/primitives/Graphics.js | 482 ++++++-- src/pixi/renderers/canvas/CanvasGraphics.js | 8 +- src/pixi/renderers/canvas/CanvasRenderer.js | 354 +++--- .../canvas/utils/CanvasMaskManager.js | 51 + .../renderers/canvas/utils/CanvasTinter.js | 243 ++++ src/pixi/renderers/webgl/WebGLBatch.js | 572 --------- src/pixi/renderers/webgl/WebGLRenderGroup.js | 1028 ----------------- src/pixi/renderers/webgl/WebGLRenderer.js | 537 ++++++--- src/pixi/renderers/webgl/WebGLShaders.js | 117 -- .../renderers/webgl/shaders/PixiFastShader.js | 146 +++ .../webgl/{ => shaders}/PixiShader.js | 147 ++- .../webgl/{ => shaders}/PrimitiveShader.js | 58 +- .../webgl/{ => shaders}/StripShader.js | 19 +- .../renderers/webgl/utils/FilterTexture.js | 84 ++ .../webgl/utils/WebGLFastSpriteBatch.js | 351 ++++++ .../webgl/{ => utils}/WebGLFilterManager.js | 319 +++-- .../webgl/{ => utils}/WebGLGraphics.js | 109 +- .../renderers/webgl/utils/WebGLMaskManager.js | 97 ++ .../webgl/utils/WebGLShaderManager.js | 161 +++ .../renderers/webgl/utils/WebGLShaderUtils.js | 55 + .../renderers/webgl/utils/WebGLSpriteBatch.js | 496 ++++++++ src/pixi/text/BitmapText.js | 51 +- src/pixi/text/Text.js | 45 +- src/pixi/textures/BaseTexture.js | 94 +- src/pixi/textures/RenderTexture.js | 174 +-- src/pixi/textures/Texture.js | 63 +- src/pixi/utils/Detector.js | 21 +- src/pixi/utils/EventTarget.js | 38 +- src/pixi/utils/Polyk.js | 31 +- src/pixi/utils/Utils.js | 105 +- 75 files changed, 5794 insertions(+), 3786 deletions(-) create mode 100644 src/pixi/InteractionData.js create mode 100644 src/pixi/display/SpriteBatch copy.js create mode 100644 src/pixi/display/SpriteBatch.js delete mode 100644 src/pixi/extras/CustomRenderable.js create mode 100644 src/pixi/filters/AlphaMaskFilter.js create mode 100644 src/pixi/filters/NormalMapFilter.js create mode 100644 src/pixi/renderers/canvas/utils/CanvasMaskManager.js create mode 100644 src/pixi/renderers/canvas/utils/CanvasTinter.js delete mode 100644 src/pixi/renderers/webgl/WebGLBatch.js delete mode 100644 src/pixi/renderers/webgl/WebGLRenderGroup.js delete mode 100644 src/pixi/renderers/webgl/WebGLShaders.js create mode 100644 src/pixi/renderers/webgl/shaders/PixiFastShader.js rename src/pixi/renderers/webgl/{ => shaders}/PixiShader.js (61%) rename src/pixi/renderers/webgl/{ => shaders}/PrimitiveShader.js (58%) rename src/pixi/renderers/webgl/{ => shaders}/StripShader.js (85%) create mode 100644 src/pixi/renderers/webgl/utils/FilterTexture.js create mode 100644 src/pixi/renderers/webgl/utils/WebGLFastSpriteBatch.js rename src/pixi/renderers/webgl/{ => utils}/WebGLFilterManager.js (62%) rename src/pixi/renderers/webgl/{ => utils}/WebGLGraphics.js (77%) create mode 100644 src/pixi/renderers/webgl/utils/WebGLMaskManager.js create mode 100644 src/pixi/renderers/webgl/utils/WebGLShaderManager.js create mode 100644 src/pixi/renderers/webgl/utils/WebGLShaderUtils.js create mode 100644 src/pixi/renderers/webgl/utils/WebGLSpriteBatch.js diff --git a/README.md b/README.md index 6e10445f..77600118 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ ![Phaser Logo](http://www.photonstorm.com/wp-content/uploads/2013/09/phaser_10_release.jpg) -Phaser 1.1.5-dev -================ +Phaser 1.2-dev +============== Phaser is a fast, free and fun open source game framework for making desktop and mobile browser HTML5 games. It uses [Pixi.js](https://github.com/GoodBoyDigital/pixi.js/) internally for fast 2D Canvas and WebGL rendering. -Version: 1.1.5 "Saldaea" - Released: -in development- +Version: 1.2 "Shienar" - Released: -in development- By Richard Davey, [Photon Storm](http://www.photonstorm.com) @@ -57,10 +57,11 @@ There is also an [un-official Getting Started Guide](http://www.antonoffplus.com Change Log ---------- -Version 1.1.5 - "Saldaea" - -in development- +Version 1.2 - "Shienar" - -in development- Significant API changes: +* Upgraded to Pixi.js 1.4.4 New features: @@ -224,10 +225,6 @@ Road Map Here is what's on our road map for the coming months: -Version 1.2 ("Shienar") - -* Update to Pixi 1.4 - this newly released build has lots of internal changes and new features we want to take advantage of. - Version 1.3 ("Tarabon") * Enhance the State Management, so you can perform non-destructive State swaps and persistence. diff --git a/src/pixi/InteractionData.js b/src/pixi/InteractionData.js new file mode 100644 index 00000000..909fcbe2 --- /dev/null +++ b/src/pixi/InteractionData.js @@ -0,0 +1,63 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * Holds all information related to an Interaction event + * + * @class InteractionData + * @constructor + */ +PIXI.InteractionData = function() +{ + /** + * This point stores the global coords of where the touch/mouse event happened + * + * @property global + * @type Point + */ + this.global = new PIXI.Point(); + + // this is here for legacy... but will remove + this.local = new PIXI.Point(); + + /** + * The target Sprite that was interacted with + * + * @property target + * @type Sprite + */ + this.target = null; + + /** + * When passed to an event handler, this will be the original DOM Event that was captured + * + * @property originalEvent + * @type Event + */ + this.originalEvent = null; +}; + +/** + * This will return the local coordinates of the specified displayObject for this InteractionData + * + * @method getLocalPosition + * @param displayObject {DisplayObject} The DisplayObject that you would like the local coords off + * @return {Point} A point containing the coordinates of the InteractionData position relative to the DisplayObject + */ +PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +{ + var worldTransform = displayObject.worldTransform; + var global = this.global; + + // do a cheeky transform to get the mouse coords; + var a00 = worldTransform.a, a01 = worldTransform.b, a02 = worldTransform.tx, + a10 = worldTransform.c, a11 = worldTransform.d, a12 = worldTransform.ty, + id = 1 / (a00 * a11 + a01 * -a10); + // set the mouse coords... + return new PIXI.Point(a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, + a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id); +}; + +// constructor +PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; \ No newline at end of file diff --git a/src/pixi/InteractionManager.js b/src/pixi/InteractionManager.js index d1e80556..e87ae3f2 100644 --- a/src/pixi/InteractionManager.js +++ b/src/pixi/InteractionManager.js @@ -4,6 +4,7 @@ /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive + * if its interactive parameter is set to true * This manager also supports multitouch. * * @class InteractionManager @@ -13,7 +14,7 @@ PIXI.InteractionManager = function(stage) { /** - * a refference to the stage + * a reference to the stage * * @property stage * @type Stage @@ -38,7 +39,6 @@ PIXI.InteractionManager = function(stage) // helpers this.tempPoint = new PIXI.Point(); - //this.tempMatrix = mat3.create(); this.mouseoverEnabled = true; @@ -58,6 +58,10 @@ PIXI.InteractionManager = function(stage) this.onTouchEnd = this.onTouchEnd.bind(this); this.onTouchMove = this.onTouchMove.bind(this); this.last = 0; + + this.currentCursorStyle = 'inherit'; + + this.mouseOut = false; }; // constructor @@ -124,36 +128,24 @@ PIXI.InteractionManager.prototype.setTarget = function(target) this.setTargetDomElement( target.view ); } - document.body.addEventListener('mouseup', this.onMouseUp, true); + }; /** - * Sets the dom element which will receive mouse/touch events. This is useful for when you have other DOM - * elements ontop of the renderers Canvas element. With this you'll be able to delegate another dom element + * Sets the DOM element which will receive mouse/touch events. This is useful for when you have other DOM + * elements on top of the renderers Canvas element. With this you'll be able to delegate another DOM element * to receive those events * * @method setTargetDomElement - * @param domElement {DOMElement} the dom element which will receive mouse and touch events + * @param domElement {DOMElement} the DOM element which will receive mouse and touch events * @private */ PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) { //remove previouse listeners - if( this.interactionDOMElement !== null ) - { - this.interactionDOMElement.style['-ms-content-zooming'] = ''; - this.interactionDOMElement.style['-ms-touch-action'] = ''; - this.interactionDOMElement.removeEventListener('mousemove', this.onMouseMove, true); - this.interactionDOMElement.removeEventListener('mousedown', this.onMouseDown, true); - this.interactionDOMElement.removeEventListener('mouseout', this.onMouseOut, true); - - // aint no multi touch just yet! - this.interactionDOMElement.removeEventListener('touchstart', this.onTouchStart, true); - this.interactionDOMElement.removeEventListener('touchend', this.onTouchEnd, true); - this.interactionDOMElement.removeEventListener('touchmove', this.onTouchMove, true); - } + this.removeEvents(); if (window.navigator.msPointerEnabled) @@ -175,6 +167,30 @@ PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) domElement.addEventListener('touchstart', this.onTouchStart, true); domElement.addEventListener('touchend', this.onTouchEnd, true); domElement.addEventListener('touchmove', this.onTouchMove, true); + + document.body.addEventListener('mouseup', this.onMouseUp, true); +}; + + +PIXI.InteractionManager.prototype.removeEvents = function() +{ + if(!this.interactionDOMElement)return; + + this.interactionDOMElement.style['-ms-content-zooming'] = ''; + this.interactionDOMElement.style['-ms-touch-action'] = ''; + + this.interactionDOMElement.removeEventListener('mousemove', this.onMouseMove, true); + this.interactionDOMElement.removeEventListener('mousedown', this.onMouseDown, true); + this.interactionDOMElement.removeEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + this.interactionDOMElement.removeEventListener('touchstart', this.onTouchStart, true); + this.interactionDOMElement.removeEventListener('touchend', this.onTouchEnd, true); + this.interactionDOMElement.removeEventListener('touchmove', this.onTouchMove, true); + + this.interactionDOMElement = null; + + document.body.removeEventListener('mouseup', this.onMouseUp, true); }; /** @@ -190,7 +206,7 @@ PIXI.InteractionManager.prototype.update = function() // frequency of 30fps?? var now = Date.now(); var diff = now - this.last; - diff = (diff * 30) / 1000; + diff = (diff * PIXI.INTERACTION_FREQUENCY ) / 1000; if(diff < 1)return; this.last = now; // @@ -199,7 +215,7 @@ PIXI.InteractionManager.prototype.update = function() // ok.. so mouse events?? // yes for now :) - // OPTIMSE - how often to check?? + // OPTIMISE - how often to check?? if(this.dirty) { this.dirty = false; @@ -220,7 +236,10 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.interactionDOMElement.style.cursor = 'inherit'; + + + var cursor = 'inherit'; + var over = false; for (i = 0; i < length; i++) { @@ -232,42 +251,55 @@ PIXI.InteractionManager.prototype.update = function() // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? // hit-test the clip! - - - if(item.mouseover || item.mouseout || item.buttonMode) + // if(item.mouseover || item.mouseout || item.buttonMode) + // { + // ok so there are some functions so lets hit test it.. + item.__hit = this.hitTest(item, this.mouse); + this.mouse.target = item; + // ok so deal with interactions.. + // looks like there was a hit! + if(item.__hit && !over) { - // ok so there are some functions so lets hit test it.. - item.__hit = this.hitTest(item, this.mouse); - this.mouse.target = item; - // ok so deal with interactions.. - // loks like there was a hit! - if(item.__hit) + if(item.buttonMode) cursor = item.defaultCursor; + + if(!item.interactiveChildren)over = true; + + if(!item.__isOver) { - if(item.buttonMode) this.interactionDOMElement.style.cursor = item.defaultCursor; - if(!item.__isOver) - { + if(item.mouseover)item.mouseover(this.mouse); + item.__isOver = true; + + // just the one! + //break; + - if(item.mouseover)item.mouseover(this.mouse); - item.__isOver = true; - } } - else + //break; + } + else + { + if(item.__isOver) { - if(item.__isOver) - { - // roll out! - if(item.mouseout)item.mouseout(this.mouse); - item.__isOver = false; - } + // roll out! + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; } } + // } // ---> } + + if( this.currentCursorStyle !== cursor ) + { + this.currentCursorStyle = cursor; + this.interactionDOMElement.style.cursor = cursor; + } + }; /** - * Is called when the mouse moves accross the renderer element + * Is called when the mouse moves across the renderer element * * @method onMouseMove * @param event {Event} The DOM event of the mouse moving @@ -307,7 +339,9 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event - // loop through inteaction tree... + if(PIXI.AUTO_PREVENT_DEFAULT)this.mouse.originalEvent.preventDefault(); + + // loop through interaction tree... // hit test each item! -> // get interactive items under point?? //stage.__i @@ -337,7 +371,13 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } }; - +/** + * Is called when the mouse button is moved out of the renderer element + * + * @method onMouseDown + * @param event {Event} The DOM event of a mouse button being moved out + * @private + */ PIXI.InteractionManager.prototype.onMouseOut = function() { var length = this.interactiveItems.length; @@ -347,7 +387,6 @@ PIXI.InteractionManager.prototype.onMouseOut = function() for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(item.__isOver) { this.mouse.target = item; @@ -355,6 +394,12 @@ PIXI.InteractionManager.prototype.onMouseOut = function() item.__isOver = false; } } + + this.mouseOut = true; + + // move the mouse to an impossible position + this.mouse.global.x = -10000; + this.mouse.global.y = -10000; }; /** @@ -366,6 +411,7 @@ PIXI.InteractionManager.prototype.onMouseOut = function() */ PIXI.InteractionManager.prototype.onMouseUp = function(event) { + this.mouse.originalEvent = event || window.event; //IE uses window.event var length = this.interactiveItems.length; @@ -375,55 +421,57 @@ PIXI.InteractionManager.prototype.onMouseUp = function(event) { var item = this.interactiveItems[i]; - if(item.mouseup || item.mouseupoutside || item.click) + //if(item.mouseup || item.mouseupoutside || item.click) + //{ + item.__hit = this.hitTest(item, this.mouse); + + if(item.__hit && !up) { - item.__hit = this.hitTest(item, this.mouse); - - if(item.__hit && !up) + //call the function! + if(item.mouseup) { - //call the function! - if(item.mouseup) - { - item.mouseup(this.mouse); - } - if(item.__isDown) - { - if(item.click)item.click(this.mouse); - } - - if(!item.interactiveChildren)up = true; + item.mouseup(this.mouse); } - else + if(item.__isDown) { - if(item.__isDown) - { - if(item.mouseupoutside)item.mouseupoutside(this.mouse); - } + if(item.click)item.click(this.mouse); } - item.__isDown = false; + if(!item.interactiveChildren)up = true; } + else + { + if(item.__isDown) + { + if(item.mouseupoutside)item.mouseupoutside(this.mouse); + } + } + + item.__isDown = false; + //} } }; /** - * Tests if the current mouse coords hit a sprite + * Tests if the current mouse coordinates hit a sprite * * @method hitTest * @param item {DisplayObject} The displayObject to test for a hit - * @param interactionData {InteractionData} The interactiondata object to update in the case of a hit + * @param interactionData {InteractionData} The interactionData object to update in the case there is a hit * @private */ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(item.vcount !== PIXI.visibleCount)return false; + if( !item.worldVisible )return false; + // temp fix for if the element is in a non visible + var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, - a00 = worldTransform[0], a01 = worldTransform[1], a02 = worldTransform[2], - a10 = worldTransform[3], a11 = worldTransform[4], a12 = worldTransform[5], + a00 = worldTransform.a, a01 = worldTransform.b, a02 = worldTransform.tx, + a10 = worldTransform.c, a11 = worldTransform.d, a12 = worldTransform.ty, id = 1 / (a00 * a11 + a01 * -a10), x = a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, y = a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id; @@ -480,10 +528,10 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) }; /** - * Is called when a touch is moved accross the renderer element + * Is called when a touch is moved across the renderer element * * @method onTouchMove - * @param event {Event} The DOM event of a touch moving accross the renderer view + * @param event {Event} The DOM event of a touch moving across the renderer view * @private */ PIXI.InteractionManager.prototype.onTouchMove = function(event) @@ -502,6 +550,10 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); + if(navigator.isCocoonJS) { + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } } var length = this.interactiveItems.length; @@ -524,6 +576,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) { var rect = this.interactionDOMElement.getBoundingClientRect(); + if(PIXI.AUTO_PREVENT_DEFAULT)event.preventDefault(); + var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { @@ -537,6 +591,10 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) this.touchs[touchEvent.identifier] = touchData; touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); + if(navigator.isCocoonJS) { + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } var length = this.interactiveItems.length; @@ -582,6 +640,10 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) var up = false; touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); + if(navigator.isCocoonJS) { + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } var length = this.interactiveItems.length; for (var j = 0; j < length; j++) @@ -633,64 +695,4 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) this.pool.push(touchData); this.touchs[touchEvent.identifier] = null; } -}; - -/** - * Holds all information related to an Interaction event - * - * @class InteractionData - * @constructor - */ -PIXI.InteractionData = function() -{ - /** - * This point stores the global coords of where the touch/mouse event happened - * - * @property global - * @type Point - */ - this.global = new PIXI.Point(); - - // this is here for legacy... but will remove - this.local = new PIXI.Point(); - - /** - * The target Sprite that was interacted with - * - * @property target - * @type Sprite - */ - this.target = null; - - /** - * When passed to an event handler, this will be the original DOM Event that was captured - * - * @property originalEvent - * @type Event - */ - this.originalEvent = null; -}; - -/** - * This will return the local coords of the specified displayObject for this InteractionData - * - * @method getLocalPosition - * @param displayObject {DisplayObject} The DisplayObject that you would like the local coords off - * @return {Point} A point containing the coords of the InteractionData position relative to the DisplayObject - */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) -{ - var worldTransform = displayObject.worldTransform; - var global = this.global; - - // do a cheeky transform to get the mouse coords; - var a00 = worldTransform[0], a01 = worldTransform[1], a02 = worldTransform[2], - a10 = worldTransform[3], a11 = worldTransform[4], a12 = worldTransform[5], - id = 1 / (a00 * a11 + a01 * -a10); - // set the mouse coords... - return new PIXI.Point(a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, - a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id); -}; - -// constructor -PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; +}; \ No newline at end of file diff --git a/src/pixi/Pixi.js b/src/pixi/Pixi.js index bc71d47c..768ef4e4 100644 --- a/src/pixi/Pixi.js +++ b/src/pixi/Pixi.js @@ -6,3 +6,41 @@ * @module PIXI */ var PIXI = PIXI || {}; + +PIXI.WEBGL_RENDERER = 0; +PIXI.CANVAS_RENDERER = 1; + +// useful for testing against if your lib is using pixi. +PIXI.VERSION = "v1.4.4"; + +// the various blend modes supported by pixi +PIXI.blendModes = { + NORMAL:0, + ADD:1, + MULTIPLY:2, + SCREEN:3, + OVERLAY:4, + DARKEN:5, + LIGHTEN:6, + COLOR_DODGE:7, + COLOR_BURN:8, + HARD_LIGHT:9, + SOFT_LIGHT:10, + DIFFERENCE:11, + EXCLUSION:12, + HUE:13, + SATURATION:14, + COLOR:15, + LUMINOSITY:16 +}; + +// the scale modes +PIXI.scaleModes = { + DEFAULT:0, + LINEAR:0, + NEAREST:1 +}; + +// interaction frequency +PIXI.INTERACTION_FREQUENCY = 30; +PIXI.AUTO_PREVENT_DEFAULT = true; \ No newline at end of file diff --git a/src/pixi/core/Circle.js b/src/pixi/core/Circle.js index d1724619..114b498a 100644 --- a/src/pixi/core/Circle.js +++ b/src/pixi/core/Circle.js @@ -3,12 +3,12 @@ */ /** - * The Circle object can be used to specify a hit area for displayobjects + * The Circle object can be used to specify a hit area for displayObjects * * @class Circle * @constructor - * @param x {Number} The X coord of the upper-left corner of the framing rectangle of this circle - * @param y {Number} The Y coord of the upper-left corner of the framing rectangle of this circle + * @param x {Number} The X coordinate of the upper-left corner of the framing rectangle of this circle + * @param y {Number} The Y coordinate of the upper-left corner of the framing rectangle of this circle * @param radius {Number} The radius of the circle */ PIXI.Circle = function(x, y, radius) @@ -47,12 +47,12 @@ PIXI.Circle.prototype.clone = function() }; /** - * Checks if the x, and y coords passed to this function are contained within this circle + * Checks whether the x, and y coordinates passed to this function are contained within this circle * * @method contains - * @param x {Number} The X coord of the point to test - * @param y {Number} The Y coord of the point to test - * @return {Boolean} if the x/y coords are within this polygon + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coordinates are within this polygon */ PIXI.Circle.prototype.contains = function(x, y) { diff --git a/src/pixi/core/Ellipse.js b/src/pixi/core/Ellipse.js index 7a9c2f64..e65313ab 100644 --- a/src/pixi/core/Ellipse.js +++ b/src/pixi/core/Ellipse.js @@ -3,12 +3,12 @@ */ /** - * The Ellipse object can be used to specify a hit area for displayobjects + * The Ellipse object can be used to specify a hit area for displayObjects * * @class Ellipse * @constructor - * @param x {Number} The X coord of the upper-left corner of the framing rectangle of this ellipse - * @param y {Number} The Y coord of the upper-left corner of the framing rectangle of this ellipse + * @param x {Number} The X coordinate of the upper-left corner of the framing rectangle of this ellipse + * @param y {Number} The Y coordinate of the upper-left corner of the framing rectangle of this ellipse * @param width {Number} The overall width of this ellipse * @param height {Number} The overall height of this ellipse */ @@ -55,12 +55,12 @@ PIXI.Ellipse.prototype.clone = function() }; /** - * Checks if the x, and y coords passed to this function are contained within this ellipse + * Checks whether the x and y coordinates passed to this function are contained within this ellipse * * @method contains - * @param x {Number} The X coord of the point to test - * @param y {Number} The Y coord of the point to test - * @return {Boolean} if the x/y coords are within this ellipse + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coords are within this ellipse */ PIXI.Ellipse.prototype.contains = function(x, y) { @@ -78,6 +78,12 @@ PIXI.Ellipse.prototype.contains = function(x, y) return (normx + normy < 0.25); }; +/** +* Returns the framing rectangle of the ellipse as a PIXI.Rectangle object +* +* @method getBounds +* @return {Rectangle} the framing rectangle +*/ PIXI.Ellipse.prototype.getBounds = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); diff --git a/src/pixi/core/Matrix.js b/src/pixi/core/Matrix.js index 1f5a67a5..5550bcf5 100644 --- a/src/pixi/core/Matrix.js +++ b/src/pixi/core/Matrix.js @@ -1,293 +1,70 @@ - - -/* - * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV - * you both rock! +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 */ -function determineMatrixArrayType() { - PIXI.Matrix = (typeof Float32Array !== 'undefined') ? Float32Array : Array; - return PIXI.Matrix; -} - -determineMatrixArrayType(); - -PIXI.mat3 = {}; - -PIXI.mat3.create = function() -{ - var matrix = new PIXI.Matrix(9); - - matrix[0] = 1; - matrix[1] = 0; - matrix[2] = 0; - matrix[3] = 0; - matrix[4] = 1; - matrix[5] = 0; - matrix[6] = 0; - matrix[7] = 0; - matrix[8] = 1; - - return matrix; +/* +* @class Matrix +* The Matrix class will choose the best type of array to use between +* a regular javascript Array and a Float32Array if the latter is available +* +*/ +PIXI.determineMatrixArrayType = function() { + return (typeof Float32Array !== 'undefined') ? Float32Array : Array; }; +PIXI.Matrix2 = PIXI.determineMatrixArrayType(); -PIXI.mat3.identity = function(matrix) +PIXI.Matrix = function() { - matrix[0] = 1; - matrix[1] = 0; - matrix[2] = 0; - matrix[3] = 0; - matrix[4] = 1; - matrix[5] = 0; - matrix[6] = 0; - matrix[7] = 0; - matrix[8] = 1; - - return matrix; + this.a = 1; + this.b = 0; + this.c = 0; + this.d = 1; + this.tx = 0; + this.ty = 0; }; - -PIXI.mat4 = {}; - -PIXI.mat4.create = function() +PIXI.Matrix.prototype.fromArray = function(array) { - var matrix = new PIXI.Matrix(16); - - matrix[0] = 1; - matrix[1] = 0; - matrix[2] = 0; - matrix[3] = 0; - matrix[4] = 0; - matrix[5] = 1; - matrix[6] = 0; - matrix[7] = 0; - matrix[8] = 0; - matrix[9] = 0; - matrix[10] = 1; - matrix[11] = 0; - matrix[12] = 0; - matrix[13] = 0; - matrix[14] = 0; - matrix[15] = 1; - - return matrix; + this.a = array[0]; + this.b = array[1]; + this.c = array[3]; + this.d = array[4]; + this.tx = array[2]; + this.ty = array[5]; }; -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.Matrix.prototype.toArray = function(transpose) { - if (!dest) { dest = mat; } + if(!this.array) this.array = new Float32Array(9); + var array = this.array; - // Cache the matrix values (makes for huge speed increases!) - var a00 = mat[0], a01 = mat[1], a02 = mat[2], - a10 = mat[3], a11 = mat[4], a12 = mat[5], - a20 = mat[6], a21 = mat[7], a22 = mat[8], - - b00 = mat2[0], b01 = mat2[1], b02 = mat2[2], - b10 = mat2[3], b11 = mat2[4], b12 = mat2[5], - b20 = mat2[6], b21 = mat2[7], b22 = mat2[8]; - - dest[0] = b00 * a00 + b01 * a10 + b02 * a20; - dest[1] = b00 * a01 + b01 * a11 + b02 * a21; - dest[2] = b00 * a02 + b01 * a12 + b02 * a22; - - dest[3] = b10 * a00 + b11 * a10 + b12 * a20; - dest[4] = b10 * a01 + b11 * a11 + b12 * a21; - dest[5] = b10 * a02 + b11 * a12 + b12 * a22; - - dest[6] = b20 * a00 + b21 * a10 + b22 * a20; - dest[7] = b20 * a01 + b21 * a11 + b22 * a21; - dest[8] = b20 * a02 + b21 * a12 + b22 * a22; - - return dest; -}; - -PIXI.mat3.clone = function(mat) -{ - var matrix = new PIXI.Matrix(9); - - matrix[0] = mat[0]; - matrix[1] = mat[1]; - matrix[2] = mat[2]; - matrix[3] = mat[3]; - matrix[4] = mat[4]; - matrix[5] = mat[5]; - matrix[6] = mat[6]; - matrix[7] = mat[7]; - matrix[8] = mat[8]; - - return matrix; -}; - -PIXI.mat3.transpose = function (mat, dest) -{ - // If we are transposing ourselves we can skip a few steps but have to cache some values - if (!dest || mat === dest) { - var a01 = mat[1], a02 = mat[2], - a12 = mat[5]; - - mat[1] = mat[3]; - mat[2] = mat[6]; - mat[3] = a01; - mat[5] = mat[7]; - mat[6] = a02; - mat[7] = a12; - return mat; - } - - dest[0] = mat[0]; - dest[1] = mat[3]; - dest[2] = mat[6]; - dest[3] = mat[1]; - dest[4] = mat[4]; - dest[5] = mat[7]; - dest[6] = mat[2]; - dest[7] = mat[5]; - dest[8] = mat[8]; - return dest; -}; - -PIXI.mat3.toMat4 = function (mat, dest) -{ - if (!dest) { dest = PIXI.mat4.create(); } - - dest[15] = 1; - dest[14] = 0; - dest[13] = 0; - dest[12] = 0; - - dest[11] = 0; - dest[10] = mat[8]; - dest[9] = mat[7]; - dest[8] = mat[6]; - - dest[7] = 0; - dest[6] = mat[5]; - dest[5] = mat[4]; - dest[4] = mat[3]; - - dest[3] = 0; - dest[2] = mat[2]; - dest[1] = mat[1]; - dest[0] = mat[0]; - - return dest; -}; - - -///// - - -PIXI.mat4.create = function() -{ - var matrix = new PIXI.Matrix(16); - - matrix[0] = 1; - matrix[1] = 0; - matrix[2] = 0; - matrix[3] = 0; - matrix[4] = 0; - matrix[5] = 1; - matrix[6] = 0; - matrix[7] = 0; - matrix[8] = 0; - matrix[9] = 0; - matrix[10] = 1; - matrix[11] = 0; - matrix[12] = 0; - matrix[13] = 0; - matrix[14] = 0; - matrix[15] = 1; - - return matrix; -}; - -PIXI.mat4.transpose = function (mat, dest) -{ - // If we are transposing ourselves we can skip a few steps but have to cache some values - if (!dest || mat === dest) + if(transpose) { - var a01 = mat[1], a02 = mat[2], a03 = mat[3], - a12 = mat[6], a13 = mat[7], - a23 = mat[11]; - - mat[1] = mat[4]; - mat[2] = mat[8]; - mat[3] = mat[12]; - mat[4] = a01; - mat[6] = mat[9]; - mat[7] = mat[13]; - mat[8] = a02; - mat[9] = a12; - mat[11] = mat[14]; - mat[12] = a03; - mat[13] = a13; - mat[14] = a23; - return mat; + this.array[0] = this.a; + this.array[1] = this.c; + this.array[2] = 0; + this.array[3] = this.b; + this.array[4] = this.d; + this.array[5] = 0; + this.array[6] = this.tx; + this.array[7] = this.ty; + this.array[8] = 1; + } + else + { + this.array[0] = this.a; + this.array[1] = this.b; + this.array[2] = this.tx; + this.array[3] = this.c; + this.array[4] = this.d; + this.array[5] = this.ty; + this.array[6] = 0; + this.array[7] = 0; + this.array[8] = 1; } - dest[0] = mat[0]; - dest[1] = mat[4]; - dest[2] = mat[8]; - dest[3] = mat[12]; - dest[4] = mat[1]; - dest[5] = mat[5]; - dest[6] = mat[9]; - dest[7] = mat[13]; - dest[8] = mat[2]; - dest[9] = mat[6]; - dest[10] = mat[10]; - dest[11] = mat[14]; - dest[12] = mat[3]; - dest[13] = mat[7]; - dest[14] = mat[11]; - dest[15] = mat[15]; - return dest; + return array;//[this.a, this.b, this.tx, this.c, this.d, this.ty, 0, 0, 1]; }; -PIXI.mat4.multiply = function (mat, mat2, dest) -{ - if (!dest) { dest = mat; } - - // Cache the matrix values (makes for huge speed increases!) - var a00 = mat[ 0], a01 = mat[ 1], a02 = mat[ 2], a03 = mat[3]; - var a10 = mat[ 4], a11 = mat[ 5], a12 = mat[ 6], a13 = mat[7]; - var a20 = mat[ 8], a21 = mat[ 9], a22 = mat[10], a23 = mat[11]; - var a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15]; - - // Cache only the current line of the second matrix - var b0 = mat2[0], b1 = mat2[1], b2 = mat2[2], b3 = mat2[3]; - dest[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - dest[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - dest[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - dest[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = mat2[4]; - b1 = mat2[5]; - b2 = mat2[6]; - b3 = mat2[7]; - dest[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - dest[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - dest[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - dest[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = mat2[8]; - b1 = mat2[9]; - b2 = mat2[10]; - b3 = mat2[11]; - dest[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - dest[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - dest[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - dest[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = mat2[12]; - b1 = mat2[13]; - b2 = mat2[14]; - b3 = mat2[15]; - dest[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - dest[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - dest[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - dest[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - return dest; -}; +PIXI.identityMatrix = new PIXI.Matrix(); \ No newline at end of file diff --git a/src/pixi/core/Point.js b/src/pixi/core/Point.js index c7e27720..a3c8e5e1 100644 --- a/src/pixi/core/Point.js +++ b/src/pixi/core/Point.js @@ -7,8 +7,8 @@ * * @class Point * @constructor - * @param x {Number} position of the point - * @param y {Number} position of the point + * @param x {Number} position of the point on the x axis + * @param y {Number} position of the point on the y axis */ PIXI.Point = function(x, y) { @@ -41,3 +41,9 @@ PIXI.Point.prototype.clone = function() // constructor PIXI.Point.prototype.constructor = PIXI.Point; +PIXI.Point.prototype.set = function(x, y) +{ + this.x = x || 0; + this.y = y || ( (y !== 0) ? this.x : 0 ) ; +}; + diff --git a/src/pixi/core/Polygon.js b/src/pixi/core/Polygon.js index 4887f6c4..b1188186 100644 --- a/src/pixi/core/Polygon.js +++ b/src/pixi/core/Polygon.js @@ -49,12 +49,12 @@ PIXI.Polygon.prototype.clone = function() }; /** - * Checks if the x, and y coords passed to this function are contained within this polygon + * Checks whether the x and y coordinates passed to this function are contained within this polygon * * @method contains - * @param x {Number} The X coord of the point to test - * @param y {Number} The Y coord of the point to test - * @return {Boolean} if the x/y coords are within this polygon + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coordinates are within this polygon */ PIXI.Polygon.prototype.contains = function(x, y) { diff --git a/src/pixi/core/Rectangle.js b/src/pixi/core/Rectangle.js index 70390a16..b8edfcce 100644 --- a/src/pixi/core/Rectangle.js +++ b/src/pixi/core/Rectangle.js @@ -55,12 +55,12 @@ PIXI.Rectangle.prototype.clone = function() }; /** - * Checks if the x, and y coords passed to this function are contained within this Rectangle + * Checks whether the x and y coordinates passed to this function are contained within this Rectangle * * @method contains - * @param x {Number} The X coord of the point to test - * @param y {Number} The Y coord of the point to test - * @return {Boolean} if the x/y coords are within this Rectangle + * @param x {Number} The X coordinate of the point to test + * @param y {Number} The Y coordinate of the point to test + * @return {Boolean} Whether the x/y coords are within this Rectangle */ PIXI.Rectangle.prototype.contains = function(x, y) { @@ -84,3 +84,4 @@ PIXI.Rectangle.prototype.contains = function(x, y) // constructor PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; +PIXI.EmptyRectangle = new PIXI.Rectangle(0,0,0,0); \ No newline at end of file diff --git a/src/pixi/display/DisplayObject.js b/src/pixi/display/DisplayObject.js index 53cce04d..09f33cd9 100644 --- a/src/pixi/display/DisplayObject.js +++ b/src/pixi/display/DisplayObject.js @@ -104,7 +104,7 @@ PIXI.DisplayObject = function() this.stage = null; /** - * [read-only] The multiplied alpha of the displayobject + * [read-only] The multiplied alpha of the displayObject * * @property worldAlpha * @type Number @@ -122,6 +122,13 @@ PIXI.DisplayObject = function() */ 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 + * + * @property defaultCursor + * @type String + * + */ this.defaultCursor = 'pointer'; /** @@ -132,20 +139,10 @@ PIXI.DisplayObject = function() * @readOnly * @private */ - this.worldTransform = PIXI.mat3.create(); //mat3.identity(); + this.worldTransform = new PIXI.Matrix(); /** - * [read-only] Current transform of the object locally - * - * @property localTransform - * @type Mat3 - * @readOnly - * @private - */ - this.localTransform = PIXI.mat3.create(); //mat3.identity(); - - /** - * [NYI] Unkown + * [NYI] Unknown * * @property color * @type Array<> @@ -162,13 +159,43 @@ PIXI.DisplayObject = function() */ this.dynamic = true; - // chach that puppy! + // cached sin rotation and cos rotation this._sr = 0; this._cr = 1; - + /** + * The area the filter is applied to + * + * @property filterArea + * @type Rectangle + */ this.filterArea = new PIXI.Rectangle(0,0,1,1); + /** + * The original, cached bounds of the object + * + * @property _bounds + * @type Rectangle + * @private + */ + this._bounds = new PIXI.Rectangle(0, 0, 1, 1); + /** + * The most up-to-date bounds of the object + * + * @property _currentBounds + * @type Rectangle + * @private + */ + this._currentBounds = null; + /** + * The original, cached mask of the object + * + * @property _currentBounds + * @type Rectangle + * @private + */ + this._mask = null; + /* * MOUSE Callbacks */ @@ -224,7 +251,7 @@ PIXI.DisplayObject = function() */ /** - * A callback that is used when the user touch's over the displayObject + * A callback that is used when the user touches over the displayObject * @method touchstart * @param interactionData {InteractionData} */ @@ -279,9 +306,30 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', { } }); +/** + * [read-only] Indicates if the sprite is globaly visible. + * + * @property worldVisible + * @type Boolean + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'worldVisible', { + get: function() { + var item = this; + + do + { + if(!item.visible)return false; + item = item.parent; + } + while(item); + + return true; + } +}); + /** * Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it. - * In PIXI a regular mask must be a PIXI.Ggraphics object. This allows for much faster masking in canvas as it utilises shape clipping. + * In PIXI a regular mask must be a PIXI.Graphics object. This allows for much faster masking in canvas as it utilises shape clipping. * To remove a mask, set this property to null. * * @property mask @@ -293,27 +341,9 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - - if(value) - { - if(this._mask) - { - value.start = this._mask.start; - value.end = this._mask.end; - } - else - { - this.addFilter(value); - value.renderable = false; - } - } - else - { - this.removeFilter(this._mask); - this._mask.renderable = true; - } - + if(this._mask)this._mask.isMask = false; this._mask = value; + if(this._mask)this._mask.isMask = true; } }); @@ -332,9 +362,6 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { if(value) { - if(this._filters)this.removeFilter(this._filters); - this.addFilter(value); - // now put all the passes in one place.. var passes = []; for (var i = 0; i < value.length; i++) @@ -346,172 +373,14 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { } } - value.start.filterPasses = passes; - } - else - { - if(this._filters) { - this.removeFilter(this._filters); - } + // TODO change this as it is legacy + this._filterBlock = {target:this, filterPasses:passes}; } this._filters = value; } }); -/* - * Adds a filter to this displayObject - * - * @method addFilter - * @param mask {Graphics} the graphics object to use as a filter - * @private - */ -PIXI.DisplayObject.prototype.addFilter = function(data) -{ - //if(this.filter)return; - //this.filter = true; -// data[0].target = this; - - - // insert a filter block.. - // TODO Onject pool thease bad boys.. - var start = new PIXI.FilterBlock(); - var end = new PIXI.FilterBlock(); - - data.start = start; - data.end = end; - - start.data = data; - end.data = data; - - start.first = start.last = this; - end.first = end.last = this; - - start.open = true; - - start.target = this; - - /* - * insert start - */ - - var childFirst = start; - var childLast = start; - var nextObject; - var previousObject; - - previousObject = this.first._iPrev; - - if(previousObject) - { - nextObject = previousObject._iNext; - childFirst._iPrev = previousObject; - previousObject._iNext = childFirst; - } - else - { - nextObject = this; - } - - if(nextObject) - { - nextObject._iPrev = childLast; - childLast._iNext = nextObject; - } - - // now insert the end filter block.. - - /* - * insert end filter - */ - childFirst = end; - childLast = end; - nextObject = null; - previousObject = null; - - previousObject = this.last; - nextObject = previousObject._iNext; - - if(nextObject) - { - nextObject._iPrev = childLast; - childLast._iNext = nextObject; - } - - childFirst._iPrev = previousObject; - previousObject._iNext = childFirst; - - var updateLast = this; - - var prevLast = this.last; - while(updateLast) - { - if(updateLast.last === prevLast) - { - updateLast.last = end; - } - updateLast = updateLast.parent; - } - - this.first = start; - - // if webGL... - if(this.__renderGroup) - { - this.__renderGroup.addFilterBlocks(start, end); - } -}; - -/* - * Removes the filter to this displayObject - * - * @method removeFilter - * @private - */ -PIXI.DisplayObject.prototype.removeFilter = function(data) -{ - //if(!this.filter)return; - //this.filter = false; - // console.log('YUOIO') - // modify the list.. - var startBlock = data.start; - - - var nextObject = startBlock._iNext; - var previousObject = startBlock._iPrev; - - if(nextObject)nextObject._iPrev = previousObject; - if(previousObject)previousObject._iNext = nextObject; - - this.first = startBlock._iNext; - - // remove the end filter - var lastBlock = data.end; - - nextObject = lastBlock._iNext; - previousObject = lastBlock._iPrev; - - if(nextObject)nextObject._iPrev = previousObject; - previousObject._iNext = nextObject; - - // this is always true too! - var tempLast = lastBlock._iPrev; - // need to make sure the parents last is updated too - var updateLast = this; - while(updateLast.last === lastBlock) - { - updateLast.last = tempLast; - updateLast = updateLast.parent; - if(!updateLast)break; - } - - // if webGL... - if(this.__renderGroup) - { - this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); - } -}; - /* * Updates the object transform for rendering * @@ -523,48 +392,139 @@ PIXI.DisplayObject.prototype.updateTransform = function() // TODO OPTIMIZE THIS!! with dirty if(this.rotation !== this.rotationCache) { + if(isNaN(parseFloat(this.rotation))) + throw new Error('DisplayObject rotation values must be numeric.'); + this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); this._cr = Math.cos(this.rotation); } - var localTransform = this.localTransform; - var parentTransform = this.parent.worldTransform; - var worldTransform = this.worldTransform; + // var localTransform = this.localTransform//.toArray(); + var parentTransform = this.parent.worldTransform;//.toArray(); + var worldTransform = this.worldTransform;//.toArray(); //console.log(localTransform) - localTransform[0] = this._cr * this.scale.x; - localTransform[1] = -this._sr * this.scale.y; - localTransform[3] = this._sr * this.scale.x; - localTransform[4] = this._cr * this.scale.y; - - // TODO --> do we even need a local matrix??? - var px = this.pivot.x; var py = this.pivot.y; - // Cache the matrix values (makes for huge speed increases!) - var a00 = localTransform[0], a01 = localTransform[1], a02 = this.position.x - localTransform[0] * px - py * localTransform[1], - a10 = localTransform[3], a11 = localTransform[4], a12 = this.position.y - localTransform[4] * py - px * localTransform[3], + var a00 = this._cr * this.scale.x, + a01 = -this._sr * this.scale.y, + a10 = this._sr * this.scale.x, + a11 = this._cr * this.scale.y, + a02 = this.position.x + a00 * px - py * a01, + a12 = this.position.y + a11 * py - px * a10, + b00 = parentTransform.a, b01 = parentTransform.b, b02 = parentTransform.tx, + b10 = parentTransform.c, b11 = parentTransform.d, b12 = parentTransform.ty; - b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], - b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; + worldTransform.a = b00 * a00 + b01 * a10; + worldTransform.b = b00 * a01 + b01 * a11; + worldTransform.tx = b00 * a02 + b01 * a12 + b02; - localTransform[2] = a02; - localTransform[5] = a12; + worldTransform.c = b10 * a00 + b11 * a10; + worldTransform.d = b10 * a01 + b11 * a11; + worldTransform.ty = b10 * a02 + b11 * a12 + b12; - worldTransform[0] = b00 * a00 + b01 * a10; - worldTransform[1] = b00 * a01 + b01 * a11; - worldTransform[2] = b00 * a02 + b01 * a12 + b02; - - worldTransform[3] = b10 * a00 + b11 * a10; - worldTransform[4] = b10 * a01 + b11 * a11; - worldTransform[5] = b10 * a02 + b11 * a12 + b12; - - // because we are using affine transformation, we can optimise the matrix concatenation process.. wooo! - // mat3.multiply(this.localTransform, this.parent.worldTransform, this.worldTransform); this.worldAlpha = this.alpha * this.parent.worldAlpha; - - this.vcount = PIXI.visibleCount; }; -PIXI.visibleCount = 0; \ No newline at end of file +/** + * Retrieves the bounds of the displayObject as a rectangle object + * + * @method getBounds + * @return {Rectangle} the rectangular bounding area + */ +PIXI.DisplayObject.prototype.getBounds = function() +{ + return PIXI.EmptyRectangle; +}; + +/** + * Retrieves the local bounds of the displayObject as a rectangle object + * + * @method getLocalBounds + * @return {Rectangle} the rectangular bounding area + */ +PIXI.DisplayObject.prototype.getLocalBounds = function() +{ + var matrixCache = this.worldTransform; + + this.worldTransform = PIXI.identityMatrix; + + this.updateTransform(); + + var bounds = this.getBounds(); + + this.worldTransform = matrixCache; + + return bounds; +}; + +/** + * Sets the object's stage reference, the stage this object is connected to + * + * @method setStageReference + * @param stage {Stage} the stage that the object will have as its current stage reference + */ +PIXI.DisplayObject.prototype.setStageReference = function(stage) +{ + this.stage = stage; + if(this._interactive)this.stage.dirty = true; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.DisplayObject.prototype._renderWebGL = function(renderSession) +{ + // OVERWRITE; + // this line is just here to pass jshinting :) + renderSession = renderSession; +}; + +/** +* Renders the object using the Canvas renderer +* +* @method _renderCanvas +* @param renderSession {RenderSession} +* @private +*/ +PIXI.DisplayObject.prototype._renderCanvas = function(renderSession) +{ + // OVERWRITE; + // this line is just here to pass jshinting :) + renderSession = renderSession; +}; + +/** + * The position of the displayObject on the x axis relative to the local coordinates of the parent. + * + * @property x + * @type Number + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'x', { + get: function() { + return this.position.x; + }, + set: function(value) { + this.position.x = value; + } +}); + +/** + * The position of the displayObject on the y axis relative to the local coordinates of the parent. + * + * @property y + * @type Number + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'y', { + get: function() { + return this.position.y; + }, + set: function(value) { + this.position.y = value; + } +}); diff --git a/src/pixi/display/DisplayObjectContainer.js b/src/pixi/display/DisplayObjectContainer.js index 44cbaa19..357dbe8c 100644 --- a/src/pixi/display/DisplayObjectContainer.js +++ b/src/pixi/display/DisplayObjectContainer.js @@ -29,6 +29,44 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; +/** + * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set + * + * @property width + * @type Number + */ + +/* +Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', { + get: function() { + return this.scale.x * this.getLocalBounds().width; + }, + set: function(value) { + this.scale.x = value / (this.getLocalBounds().width/this.scale.x); + this._width = value; + } +}); +*/ + +/** + * The height of the displayObjectContainer, setting this will actually modify the scale to achieve the value set + * + * @property height + * @type Number + */ + + /* +Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', { + get: function() { + return this.scale.y * this.getLocalBounds().height; + }, + set: function(value) { + this.scale.y = value / (this.getLocalBounds().height/this.scale.y); + this._height = value; + } +}); +*/ + /** * Adds a child to the container. * @@ -37,82 +75,7 @@ PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; */ PIXI.DisplayObjectContainer.prototype.addChild = function(child) { - if(child.parent && child.parent !== this) - { - //// COULD BE THIS??? - child.parent.removeChild(child); - // return; - } - - child.parent = this; - - this.children.push(child); - - // update the stage refference.. - - if(this.stage) - { - var tmpChild = child; - do - { - if(tmpChild.interactive)this.stage.dirty = true; - tmpChild.stage = this.stage; - tmpChild = tmpChild._iNext; - } - while(tmpChild); - } - - // LINKED LIST // - - // modify the list.. - var childFirst = child.first; - var childLast = child.last; - var nextObject; - var previousObject; - - // this could be wrong if there is a filter?? - if(this._filters || this._mask) - { - previousObject = this.last._iPrev; - } - else - { - previousObject = this.last; - } - - nextObject = previousObject._iNext; - - // always true in this case - // need to make sure the parents last is updated too - var updateLast = this; - var prevLast = previousObject; - - while(updateLast) - { - if(updateLast.last === prevLast) - { - updateLast.last = child.last; - } - updateLast = updateLast.parent; - } - - if(nextObject) - { - nextObject._iPrev = childLast; - childLast._iNext = nextObject; - } - - childFirst._iPrev = previousObject; - previousObject._iNext = childFirst; - - // need to remove any render groups.. - if(this.__renderGroup) - { - // being used by a renderTexture.. if it exists then it must be from a render texture; - if(child.__renderGroup)child.__renderGroup.removeDisplayObjectAndChildren(child); - // add them to the new render group.. - this.__renderGroup.addDisplayObjectAndChildren(child); - } + this.addChildAt(child, this.children.length); }; /** @@ -126,76 +89,16 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) { if(index >= 0 && index <= this.children.length) { - if(child.parent !== undefined) + if(child.parent) { child.parent.removeChild(child); } child.parent = this; - if(this.stage) - { - var tmpChild = child; - do - { - if(tmpChild.interactive)this.stage.dirty = true; - tmpChild.stage = this.stage; - tmpChild = tmpChild._iNext; - } - while(tmpChild); - } - - // modify the list.. - var childFirst = child.first; - var childLast = child.last; - var nextObject; - var previousObject; - - if(index === this.children.length) - { - previousObject = this.last; - var updateLast = this; - var prevLast = this.last; - while(updateLast) - { - if(updateLast.last === prevLast) - { - updateLast.last = child.last; - } - updateLast = updateLast.parent; - } - } - else if(index === 0) - { - previousObject = this; - } - else - { - previousObject = this.children[index-1].last; - } - - nextObject = previousObject._iNext; - - // always true in this case - if(nextObject) - { - nextObject._iPrev = childLast; - childLast._iNext = nextObject; - } - - childFirst._iPrev = previousObject; - previousObject._iNext = childFirst; - this.children.splice(index, 0, child); - // need to remove any render groups.. - if(this.__renderGroup) - { - // being used by a renderTexture.. if it exists then it must be from a render texture; - if(child.__renderGroup)child.__renderGroup.removeDisplayObjectAndChildren(child); - // add them to the new render group.. - this.__renderGroup.addDisplayObjectAndChildren(child); - } + if(this.stage)child.setStageReference(this.stage); } else { @@ -224,23 +127,13 @@ PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.'); } - this.removeChild(child); - this.removeChild(child2); - - if(index1 < index2) - { - this.addChildAt(child2, index1); - this.addChildAt(child, index2); - } - else - { - this.addChildAt(child, index2); - this.addChildAt(child2, index1); - } + this.children[index1] = child2; + this.children[index2] = child; + }; /** - * Returns the Child at the specified index + * Returns the child at the specified index * * @method getChildAt * @param index {Number} The index to get the child from @@ -253,7 +146,7 @@ PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) } else { - throw new Error('Both the supplied DisplayObjects must be a child of the caller ' + this); + throw new Error('The supplied DisplayObjects must be a child of the caller ' + this); } }; @@ -268,53 +161,8 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) var index = this.children.indexOf( child ); if ( index !== -1 ) { - // unlink // - // modify the list.. - var childFirst = child.first; - var childLast = child.last; - - var nextObject = childLast._iNext; - var previousObject = childFirst._iPrev; - - if(nextObject)nextObject._iPrev = previousObject; - previousObject._iNext = nextObject; - - if(this.last === childLast) - { - var tempLast = childFirst._iPrev; - // need to make sure the parents last is updated too - var updateLast = this; - - while(updateLast.last === childLast) - { - updateLast.last = tempLast; - updateLast = updateLast.parent; - if(!updateLast)break; - - } - } - - childLast._iNext = null; - childFirst._iPrev = null; - // update the stage reference.. - if(this.stage) - { - var tmpChild = child; - do - { - if(tmpChild.interactive)this.stage.dirty = true; - tmpChild.stage = null; - tmpChild = tmpChild._iNext; - } - while(tmpChild); - } - - // webGL trim - if(child.__renderGroup) - { - child.__renderGroup.removeDisplayObjectAndChildren(child); - } + if(this.stage)child.removeStageReference(); child.parent = undefined; this.children.splice( index, 1 ); @@ -326,13 +174,15 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) }; /* - * Updates the container's children's transform for rendering + * Updates the container's childrens transform for rendering * * @method updateTransform * @private */ PIXI.DisplayObjectContainer.prototype.updateTransform = function() { + //this._currentBounds = null; + if(!this.visible)return; PIXI.DisplayObject.prototype.updateTransform.call( this ); @@ -342,3 +192,181 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } }; + +/** + * Retrieves the bounds of the displayObjectContainer as a rectangle object + * + * @method getBounds + * @return {Rectangle} the rectangular bounding area + */ +PIXI.DisplayObjectContainer.prototype.getBounds = function() +{ + if(this.children.length === 0)return PIXI.EmptyRectangle; + + // TODO the bounds have already been calculated this render session so return what we have + + + var minX = Infinity; + var minY = Infinity; + + var maxX = -Infinity; + var maxY = -Infinity; + + var childBounds; + var childMaxX; + var childMaxY; + + var childVisible = false; + + for(var i=0,j=this.children.length; i childMaxX ? maxX : childMaxX; + maxY = maxY > childMaxY ? maxY : childMaxY; + } + + if(!childVisible) + return PIXI.EmptyRectangle; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.y = minY; + bounds.width = maxX - minX; + bounds.height = maxY - minY; + + // TODO: store a reference so that if this function gets called again in the render cycle we do not have to recalculate + //this._currentBounds = bounds; + + return bounds; +}; + +/** + * Sets the container's stage reference, the stage this object is connected to + * + * @method setStageReference + * @param stage {Stage} the stage that the container will have as its current stage reference + */ +PIXI.DisplayObjectContainer.prototype.setStageReference = function(stage) +{ + this.stage = stage; + if(this._interactive)this.stage.dirty = true; + + for(var i=0,j=this.children.length; i maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + // store a reference so that if this function gets called again in the render cycle we do not have to recalculate + this._currentBounds = bounds; + + return bounds; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Sprite.prototype._renderWebGL = function(renderSession) +{ + // 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; + + var i,j; + + // do a quick check to see if this element has a mask or a filter. + if(this._mask || this._filters) + { + var spriteBatch = renderSession.spriteBatch; + + if(this._mask) + { + spriteBatch.stop(); + renderSession.maskManager.pushMask(this.mask, renderSession); + spriteBatch.start(); + } + + if(this._filters) + { + spriteBatch.flush(); + renderSession.filterManager.pushFilter(this._filterBlock); + } + + // add this sprite to the batch + spriteBatch.render(this); + + // now loop through the children and make sure they get rendered + for(i=0,j=this.children.length; i maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + // store a reference so that if this function gets called again in the render cycle we do not have to recalculate + this._currentBounds = bounds; + + return bounds; +}; + + +PIXI.TilingSprite.prototype.generateTilingTexture = function(forcePowerOfTwo) +{ + var texture = this.texture; + + if(!texture.baseTexture.hasLoaded)return; + + var baseTexture = texture.baseTexture; + var frame = texture.frame; + + var targetWidth, targetHeight; + + // check that the frame is the same size as the base texture. + + var isFrame = frame.width !== baseTexture.width || frame.height !== baseTexture.height; + + this.tilingTexture = texture; + + var newTextureRequired = false; + + if(!forcePowerOfTwo) + { + if(isFrame) + { + targetWidth = frame.width; + targetHeight = frame.height; + + newTextureRequired = true; + } + } + else + { + targetWidth = PIXI.getNextPowerOfTwo(texture.frame.width); + targetHeight = PIXI.getNextPowerOfTwo(texture.frame.height); + + if(frame.width !== targetWidth && frame.height !== targetHeight)newTextureRequired = true; + } + + if(newTextureRequired) + { + var canvasBuffer = new PIXI.CanvasBuffer(targetWidth, targetHeight); + + canvasBuffer.context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + targetWidth, + targetHeight); + + this.tilingTexture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + + this.tileScaleOffset.x = frame.width / targetWidth; + this.tileScaleOffset.y = frame.height / targetHeight; + } + + + this.tilingTexture.baseTexture._powerOf2 = true; +}; \ No newline at end of file diff --git a/src/pixi/filters/AbstractFilter.js b/src/pixi/filters/AbstractFilter.js index 43665de4..f9ca0b3c 100644 --- a/src/pixi/filters/AbstractFilter.js +++ b/src/pixi/filters/AbstractFilter.js @@ -3,7 +3,7 @@ */ /** - * This is the base class for creating a pixi.js filter. Currently only webGL supports filters. + * This is the base class for creating a pixi.js filter. Currently only webGL supports filters. * If you want to make a custom filter this should be your base class. * @class AbstractFilter * @constructor @@ -21,15 +21,26 @@ PIXI.AbstractFilter = function(fragmentSrc, uniforms) */ this.passes = [this]; - + /** + * @property shaders + * @type Array an array of shaders + * @private + */ + this.shaders = []; + this.dirty = true; this.padding = 0; /** - @property uniforms - @private + * @property uniforms + * @type object + * @private */ this.uniforms = uniforms || {}; - + /** + * @property fragmentSrc + * @type Array + * @private + */ this.fragmentSrc = fragmentSrc || []; }; diff --git a/src/pixi/filters/AlphaMaskFilter.js b/src/pixi/filters/AlphaMaskFilter.js new file mode 100644 index 00000000..5d694128 --- /dev/null +++ b/src/pixi/filters/AlphaMaskFilter.js @@ -0,0 +1,93 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g propery of the texture is used to offset the y. + * @class AlphaMaskFilter + * @contructor + * @param texture {Texture} The texture used for the displacemtent map * must be power of 2 texture at the moment + */ +PIXI.AlphaMaskFilter = function(texture) +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + texture.baseTexture._powerOf2 = true; + + // set the uniforms + //console.log() + this.uniforms = { + mask: {type: 'sampler2D', value:texture}, + mapDimensions: {type: '2f', value:{x:1, y:5112}}, + dimensions: {type: '4fv', value:[0,0,0,0]} + }; + + if(texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D mask;', + 'uniform sampler2D uSampler;', + 'uniform vec2 offset;', + 'uniform vec4 dimensions;', + 'uniform vec2 mapDimensions;', + + 'void main(void) {', + ' vec2 mapCords = vTextureCoord.xy;', + ' mapCords += (dimensions.zw + offset)/ dimensions.xy ;', + ' mapCords.y *= -1.0;', + ' mapCords.y += 1.0;', + ' mapCords *= dimensions.xy / mapDimensions;', + + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' float maskAlpha = texture2D(mask, mapCords).r;', + ' original *= maskAlpha;', + //' original.rgb *= maskAlpha;', + ' gl_FragColor = original;', + //' gl_FragColor = gl_FragColor;', + '}' + ]; +}; + +PIXI.AlphaMaskFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.AlphaMaskFilter.prototype.constructor = PIXI.AlphaMaskFilter; + +PIXI.AlphaMaskFilter.prototype.onTextureLoaded = function() +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +/** + * The texture used for the displacemtent map * must be power of 2 texture at the moment + * + * @property map + * @type Texture + */ +Object.defineProperty(PIXI.AlphaMaskFilter.prototype, 'map', { + get: function() { + return this.uniforms.mask.value; + }, + set: function(value) { + this.uniforms.mask.value = value; + } +}); + diff --git a/src/pixi/filters/BlurFilter.js b/src/pixi/filters/BlurFilter.js index 5f89bff8..1b5cddb8 100644 --- a/src/pixi/filters/BlurFilter.js +++ b/src/pixi/filters/BlurFilter.js @@ -35,7 +35,7 @@ Object.defineProperty(PIXI.BlurFilter.prototype, 'blur', { }); /** - * Sets the strength of the blurX property simultaneously + * Sets the strength of the blurX property * * @property blurX * @type Number the strength of the blurX @@ -51,7 +51,7 @@ Object.defineProperty(PIXI.BlurFilter.prototype, 'blurX', { }); /** - * Sets the strength of the blurX property simultaneously + * Sets the strength of the blurX property * * @property blurY * @type Number the strength of the blurY diff --git a/src/pixi/filters/BlurXFilter.js b/src/pixi/filters/BlurXFilter.js index 6e7be332..35837919 100644 --- a/src/pixi/filters/BlurXFilter.js +++ b/src/pixi/filters/BlurXFilter.js @@ -16,7 +16,7 @@ PIXI.BlurXFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform float blur;', 'uniform sampler2D uSampler;', diff --git a/src/pixi/filters/BlurYFilter.js b/src/pixi/filters/BlurYFilter.js index 99829fb7..82a74602 100644 --- a/src/pixi/filters/BlurYFilter.js +++ b/src/pixi/filters/BlurYFilter.js @@ -16,7 +16,7 @@ PIXI.BlurYFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform float blur;', 'uniform sampler2D uSampler;', diff --git a/src/pixi/filters/ColorMatrixFilter.js b/src/pixi/filters/ColorMatrixFilter.js index 122b6fbd..8d08eea3 100644 --- a/src/pixi/filters/ColorMatrixFilter.js +++ b/src/pixi/filters/ColorMatrixFilter.js @@ -27,14 +27,14 @@ PIXI.ColorMatrixFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform float invert;', 'uniform mat4 matrix;', 'uniform sampler2D uSampler;', 'void main(void) {', ' gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;', - ' gl_FragColor = gl_FragColor * vColor;', + // ' gl_FragColor = gl_FragColor;', '}' ]; }; diff --git a/src/pixi/filters/ColorStepFilter.js b/src/pixi/filters/ColorStepFilter.js index c83c887b..e8f76505 100644 --- a/src/pixi/filters/ColorStepFilter.js +++ b/src/pixi/filters/ColorStepFilter.js @@ -4,7 +4,7 @@ /** * - * This turns your displayObjects to black and white. + * This lowers the color depth of your image by the given amount, producing an image with a smaller palette. * @class ColorStepFilter * @contructor */ @@ -22,14 +22,14 @@ PIXI.ColorStepFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform sampler2D uSampler;', 'uniform float step;', 'void main(void) {', ' vec4 color = texture2D(uSampler, vTextureCoord);', ' color = floor(color * step) / step;', - ' gl_FragColor = color * vColor;', + ' gl_FragColor = color;', '}' ]; }; diff --git a/src/pixi/filters/CrossHatchFilter.js b/src/pixi/filters/CrossHatchFilter.js index 43f3c261..3426b544 100644 --- a/src/pixi/filters/CrossHatchFilter.js +++ b/src/pixi/filters/CrossHatchFilter.js @@ -16,7 +16,7 @@ PIXI.CrossHatchFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform float blur;', 'uniform sampler2D uSampler;', diff --git a/src/pixi/filters/DisplacementFilter.js b/src/pixi/filters/DisplacementFilter.js index 21853fd5..f36447b3 100644 --- a/src/pixi/filters/DisplacementFilter.js +++ b/src/pixi/filters/DisplacementFilter.js @@ -19,6 +19,7 @@ PIXI.DisplacementFilter = function(texture) texture.baseTexture._powerOf2 = true; // set the uniforms + //console.log() this.uniforms = { displacementMap: {type: 'sampler2D', value:texture}, scale: {type: '2f', value:{x:30, y:30}}, @@ -42,7 +43,7 @@ PIXI.DisplacementFilter = function(texture) this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform sampler2D displacementMap;', 'uniform sampler2D uSampler;', 'uniform vec2 scale;', @@ -66,7 +67,7 @@ PIXI.DisplacementFilter = function(texture) ' vec2 cord = vTextureCoord;', //' gl_FragColor = texture2D(displacementMap, cord);', - ' gl_FragColor = gl_FragColor * vColor;', + // ' gl_FragColor = gl_FragColor;', '}' ]; }; diff --git a/src/pixi/filters/DotScreenFilter.js b/src/pixi/filters/DotScreenFilter.js index 7a684a13..8c9a49fd 100644 --- a/src/pixi/filters/DotScreenFilter.js +++ b/src/pixi/filters/DotScreenFilter.js @@ -5,8 +5,8 @@ /** * - * This filter applies a pixlate effect making display objects appear 'blocky' - * @class PixelateFilter + * This filter applies a dotscreen effect making display objects appear to be made out of black and white halftone dots like an old printer + * @class DotScreenFilter * @contructor */ PIXI.DotScreenFilter = function() @@ -25,7 +25,7 @@ PIXI.DotScreenFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform vec4 dimensions;', 'uniform sampler2D uSampler;', diff --git a/src/pixi/filters/GrayFilter.js b/src/pixi/filters/GrayFilter.js index 5065834a..34a52b14 100644 --- a/src/pixi/filters/GrayFilter.js +++ b/src/pixi/filters/GrayFilter.js @@ -22,14 +22,14 @@ PIXI.GrayFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform sampler2D uSampler;', 'uniform float gray;', 'void main(void) {', ' gl_FragColor = texture2D(uSampler, vTextureCoord);', ' gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.2126*gl_FragColor.r + 0.7152*gl_FragColor.g + 0.0722*gl_FragColor.b), gray);', - ' gl_FragColor = gl_FragColor * vColor;', + // ' gl_FragColor = gl_FragColor;', '}' ]; }; diff --git a/src/pixi/filters/InvertFilter.js b/src/pixi/filters/InvertFilter.js index ac4393f9..37780483 100644 --- a/src/pixi/filters/InvertFilter.js +++ b/src/pixi/filters/InvertFilter.js @@ -22,7 +22,7 @@ PIXI.InvertFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform float invert;', 'uniform sampler2D uSampler;', @@ -30,7 +30,7 @@ PIXI.InvertFilter = function() ' gl_FragColor = texture2D(uSampler, vTextureCoord);', ' gl_FragColor.rgb = mix( (vec3(1)-gl_FragColor.rgb) * gl_FragColor.a, gl_FragColor.rgb, 1.0 - invert);', //' gl_FragColor.rgb = gl_FragColor.rgb * gl_FragColor.a;', - ' gl_FragColor = gl_FragColor * vColor;', + // ' gl_FragColor = gl_FragColor * vColor;', '}' ]; }; diff --git a/src/pixi/filters/NormalMapFilter.js b/src/pixi/filters/NormalMapFilter.js new file mode 100644 index 00000000..ca24c244 --- /dev/null +++ b/src/pixi/filters/NormalMapFilter.js @@ -0,0 +1,225 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * + * The NormalMapFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used offset the x and the g propery of the texture is used to offset the y. + * @class NormalMapFilter + * @contructor + * @param texture {Texture} The texture used for the displacemtent map * must be power of 2 texture at the moment + */ +PIXI.NormalMapFilter = function(texture) +{ + PIXI.AbstractFilter.call( this ); + + this.passes = [this]; + texture.baseTexture._powerOf2 = true; + + // set the uniforms + //console.log() + this.uniforms = { + displacementMap: {type: 'sampler2D', value:texture}, + scale: {type: '2f', value:{x:15, y:15}}, + offset: {type: '2f', value:{x:0, y:0}}, + mapDimensions: {type: '2f', value:{x:1, y:1}}, + dimensions: {type: '4f', value:[0,0,0,0]}, + // LightDir: {type: 'f3', value:[0, 1, 0]}, + LightPos: {type: '3f', value:[0, 1, 0]} + }; + + + if(texture.baseTexture.hasLoaded) + { + this.uniforms.mapDimensions.value.x = texture.width; + this.uniforms.mapDimensions.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on("loaded", this.boundLoadedFunction); + } + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D displacementMap;", + "uniform sampler2D uSampler;", + + "uniform vec4 dimensions;", + + "const vec2 Resolution = vec2(1.0,1.0);", //resolution of screen + "uniform vec3 LightPos;", //light position, normalized + "const vec4 LightColor = vec4(1.0, 1.0, 1.0, 1.0);", //light RGBA -- alpha is intensity + "const vec4 AmbientColor = vec4(1.0, 1.0, 1.0, 0.5);", //ambient RGBA -- alpha is intensity + "const vec3 Falloff = vec3(0.0, 1.0, 0.2);", //attenuation coefficients + + "uniform vec3 LightDir;",//" = vec3(1.0, 0.0, 1.0);", + + + "uniform vec2 mapDimensions;",// = vec2(256.0, 256.0);", + + + "void main(void) {", + "vec2 mapCords = vTextureCoord.xy;", + + "vec4 color = texture2D(uSampler, vTextureCoord.st);", + "vec3 nColor = texture2D(displacementMap, vTextureCoord.st).rgb;", + + + "mapCords *= vec2(dimensions.x/512.0, dimensions.y/512.0);", + + "mapCords.y *= -1.0;", + "mapCords.y += 1.0;", + + //RGBA of our diffuse color + "vec4 DiffuseColor = texture2D(uSampler, vTextureCoord);", + + //RGB of our normal map + "vec3 NormalMap = texture2D(displacementMap, mapCords).rgb;", + + //The delta position of light + //"vec3 LightDir = vec3(LightPos.xy - (gl_FragCoord.xy / Resolution.xy), LightPos.z);", + "vec3 LightDir = vec3(LightPos.xy - (mapCords.xy), LightPos.z);", + //Correct for aspect ratio + //"LightDir.x *= Resolution.x / Resolution.y;", + + //Determine distance (used for attenuation) BEFORE we normalize our LightDir + "float D = length(LightDir);", + + //normalize our vectors + "vec3 N = normalize(NormalMap * 2.0 - 1.0);", + "vec3 L = normalize(LightDir);", + + //Pre-multiply light color with intensity + //Then perform "N dot L" to determine our diffuse term + "vec3 Diffuse = (LightColor.rgb * LightColor.a) * max(dot(N, L), 0.0);", + + //pre-multiply ambient color with intensity + "vec3 Ambient = AmbientColor.rgb * AmbientColor.a;", + + //calculate attenuation + "float Attenuation = 1.0 / ( Falloff.x + (Falloff.y*D) + (Falloff.z*D*D) );", + + //the calculation which brings it all together + "vec3 Intensity = Ambient + Diffuse * Attenuation;", + "vec3 FinalColor = DiffuseColor.rgb * Intensity;", + "gl_FragColor = vColor * vec4(FinalColor, DiffuseColor.a);", + //"gl_FragColor = vec4(1.0, 0.0, 0.0, Attenuation);",//vColor * vec4(FinalColor, DiffuseColor.a);", + /* + // normalise color + "vec3 normal = normalize(nColor * 2.0 - 1.0);", + + "vec3 deltaPos = vec3( (light.xy - gl_FragCoord.xy) / resolution.xy, light.z );", + + "float lambert = clamp(dot(normal, lightDir), 0.0, 1.0);", + + "float d = sqrt(dot(deltaPos, deltaPos));", + "float att = 1.0 / ( attenuation.x + (attenuation.y*d) + (attenuation.z*d*d) );", + + "vec3 result = (ambientColor * ambientIntensity) + (lightColor.rgb * lambert) * att;", + "result *= color.rgb;", + + "gl_FragColor = vec4(result, 1.0);",*/ + + + + "}" + ]; + +} + +/* +void main() { + //sample color & normals from our textures + vec4 color = texture2D(u_texture, v_texCoords.st); + vec3 nColor = texture2D(u_normals, v_texCoords.st).rgb; + + //some bump map programs will need the Y value flipped.. + nColor.g = yInvert ? 1.0 - nColor.g : nColor.g; + + //this is for debugging purposes, allowing us to lower the intensity of our bump map + vec3 nBase = vec3(0.5, 0.5, 1.0); + nColor = mix(nBase, nColor, strength); + + //normals need to be converted to [-1.0, 1.0] range and normalized + vec3 normal = normalize(nColor * 2.0 - 1.0); + + //here we do a simple distance calculation + vec3 deltaPos = vec3( (light.xy - gl_FragCoord.xy) / resolution.xy, light.z ); + + vec3 lightDir = normalize(deltaPos); + float lambert = useNormals ? clamp(dot(normal, lightDir), 0.0, 1.0) : 1.0; + + //now let's get a nice little falloff + float d = sqrt(dot(deltaPos, deltaPos)); + float att = useShadow ? 1.0 / ( attenuation.x + (attenuation.y*d) + (attenuation.z*d*d) ) : 1.0; + + vec3 result = (ambientColor * ambientIntensity) + (lightColor.rgb * lambert) * att; + result *= color.rgb; + + gl_FragColor = v_color * vec4(result, color.a); +} +*/ +PIXI.NormalMapFilter.prototype = Object.create( PIXI.AbstractFilter.prototype ); +PIXI.NormalMapFilter.prototype.constructor = PIXI.NormalMapFilter; + +PIXI.NormalMapFilter.prototype.onTextureLoaded = function() +{ + + this.uniforms.mapDimensions.value.x = this.uniforms.displacementMap.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.displacementMap.value.height; + + this.uniforms.displacementMap.value.baseTexture.off("loaded", this.boundLoadedFunction) + +} + +/** + * The texture used for the displacemtent map * must be power of 2 texture at the moment + * + * @property map + * @type Texture + */ +Object.defineProperty(PIXI.NormalMapFilter.prototype, 'map', { + get: function() { + return this.uniforms.displacementMap.value; + }, + set: function(value) { + this.uniforms.displacementMap.value = value; + } +}); + +/** + * The multiplier used to scale the displacement result from the map calculation. + * + * @property scale + * @type Point + */ +Object.defineProperty(PIXI.NormalMapFilter.prototype, 'scale', { + get: function() { + return this.uniforms.scale.value; + }, + set: function(value) { + this.uniforms.scale.value = value; + } +}); + +/** + * The offset used to move the displacement map. + * + * @property offset + * @type Point + */ +Object.defineProperty(PIXI.NormalMapFilter.prototype, 'offset', { + get: function() { + return this.uniforms.offset.value; + }, + set: function(value) { + this.uniforms.offset.value = value; + } +}); \ No newline at end of file diff --git a/src/pixi/filters/PixelateFilter.js b/src/pixi/filters/PixelateFilter.js index e4320ebf..89a146b8 100644 --- a/src/pixi/filters/PixelateFilter.js +++ b/src/pixi/filters/PixelateFilter.js @@ -4,7 +4,7 @@ /** * - * This filter applies a pixlate effect making display objects appear 'blocky' + * This filter applies a pixelate effect making display objects appear 'blocky' * @class PixelateFilter * @contructor */ @@ -24,7 +24,7 @@ PIXI.PixelateFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform vec2 testDim;', 'uniform vec4 dimensions;', 'uniform vec2 pixelSize;', diff --git a/src/pixi/filters/RGBSplitFilter.js b/src/pixi/filters/RGBSplitFilter.js index 81439dd5..fba3bde9 100644 --- a/src/pixi/filters/RGBSplitFilter.js +++ b/src/pixi/filters/RGBSplitFilter.js @@ -19,7 +19,7 @@ PIXI.RGBSplitFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform vec2 red;', 'uniform vec2 green;', 'uniform vec2 blue;', diff --git a/src/pixi/filters/SepiaFilter.js b/src/pixi/filters/SepiaFilter.js index f114d761..9aff3054 100644 --- a/src/pixi/filters/SepiaFilter.js +++ b/src/pixi/filters/SepiaFilter.js @@ -22,7 +22,7 @@ PIXI.SepiaFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform float sepia;', 'uniform sampler2D uSampler;', @@ -31,7 +31,7 @@ PIXI.SepiaFilter = function() 'void main(void) {', ' gl_FragColor = texture2D(uSampler, vTextureCoord);', ' gl_FragColor.rgb = mix( gl_FragColor.rgb, gl_FragColor.rgb * sepiaMatrix, sepia);', - ' gl_FragColor = gl_FragColor * vColor;', + // ' gl_FragColor = gl_FragColor * vColor;', '}' ]; }; diff --git a/src/pixi/filters/TwistFilter.js b/src/pixi/filters/TwistFilter.js index c529dd78..01c51690 100644 --- a/src/pixi/filters/TwistFilter.js +++ b/src/pixi/filters/TwistFilter.js @@ -4,8 +4,8 @@ /** * - * This filter applies a pixlate effect making display objects appear 'blocky' - * @class PixelateFilter + * This filter applies a twist effect making display objects appear twisted in the given direction + * @class TwistFilter * @contructor */ PIXI.TwistFilter = function() @@ -24,7 +24,7 @@ PIXI.TwistFilter = function() this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform vec4 dimensions;', 'uniform sampler2D uSampler;', diff --git a/src/pixi/loaders/AssetLoader.js b/src/pixi/loaders/AssetLoader.js index c2e8ef18..0dda7502 100644 --- a/src/pixi/loaders/AssetLoader.js +++ b/src/pixi/loaders/AssetLoader.js @@ -70,7 +70,12 @@ PIXI.AssetLoader = function(assetURLs, crossorigin) // constructor PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; - +/** + * Given a filename, returns its extension, wil + * + * @method _getDataType + * @param str {String} the name of the asset + */ PIXI.AssetLoader.prototype._getDataType = function(str) { var test = 'data:'; diff --git a/src/pixi/loaders/AtlasLoader.js b/src/pixi/loaders/AtlasLoader.js index 68300ebd..98e32c99 100644 --- a/src/pixi/loaders/AtlasLoader.js +++ b/src/pixi/loaders/AtlasLoader.js @@ -3,9 +3,9 @@ */ /** - * The atlas file loader is used to load in Atlas data and parsing it + * The atlas file loader is used to load in Atlas data and parse it * When loaded this class will dispatch a 'loaded' event - * If load failed this class will dispatch a 'error' event + * If loading fails this class will dispatch an 'error' event * @class AtlasLoader * @extends EventTarget * @constructor @@ -25,8 +25,11 @@ PIXI.AtlasLoader = function (url, crossorigin) { // constructor PIXI.AtlasLoader.constructor = PIXI.AtlasLoader; -/** - * This will begin loading the JSON file + + /** + * Starts loading the JSON file + * + * @method load */ PIXI.AtlasLoader.prototype.load = function () { this.ajaxRequest = new PIXI.AjaxRequest(); @@ -39,6 +42,7 @@ PIXI.AtlasLoader.prototype.load = function () { /** * Invoke when JSON file is loaded + * @method onAtlasLoaded * @private */ PIXI.AtlasLoader.prototype.onAtlasLoaded = function () { @@ -156,7 +160,8 @@ PIXI.AtlasLoader.prototype.onAtlasLoaded = function () { }; /** - * Invoke when json file loaded + * Invoke when json file has loaded + * @method onLoaded * @private */ PIXI.AtlasLoader.prototype.onLoaded = function () { @@ -174,6 +179,7 @@ PIXI.AtlasLoader.prototype.onLoaded = function () { /** * Invoke when error occured + * @method onError * @private */ PIXI.AtlasLoader.prototype.onError = function () { diff --git a/src/pixi/loaders/BitmapFontLoader.js b/src/pixi/loaders/BitmapFontLoader.js index 8421f90a..78488d2f 100644 --- a/src/pixi/loaders/BitmapFontLoader.js +++ b/src/pixi/loaders/BitmapFontLoader.js @@ -17,7 +17,7 @@ PIXI.BitmapFontLoader = function(url, crossorigin) { /* - * i use texture packer to load the assets.. + * I use texture packer to load the assets.. * http://www.codeandweb.com/texturepacker * make sure to set the format as 'JSON' */ @@ -67,7 +67,7 @@ PIXI.BitmapFontLoader.prototype.constructor = PIXI.BitmapFontLoader; */ PIXI.BitmapFontLoader.prototype.load = function() { - this.ajaxRequest = new XMLHttpRequest(); + this.ajaxRequest = new PIXI.AjaxRequest(); var scope = this; this.ajaxRequest.onreadystatechange = function() { @@ -80,7 +80,7 @@ PIXI.BitmapFontLoader.prototype.load = function() }; /** - * Invoked when XML file is loaded, parses the data + * Invoked when the XML file is loaded, parses the data * * @method onXMLLoaded * @private @@ -91,36 +91,48 @@ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() { if (this.ajaxRequest.status === 200 || window.location.protocol.indexOf('http') === -1) { - var textureUrl = this.baseUrl + this.ajaxRequest.responseXML.getElementsByTagName('page')[0].attributes.getNamedItem('file').nodeValue; + var responseXML = this.ajaxRequest.responseXML; + if(!responseXML || /MSIE 9/i.test(navigator.userAgent) || navigator.isCocoonJS) { + if(typeof(window.DOMParser) === 'function') { + var domparser = new DOMParser(); + responseXML = domparser.parseFromString(this.ajaxRequest.responseText, 'text/xml'); + } else { + var div = document.createElement('div'); + div.innerHTML = this.ajaxRequest.responseText; + responseXML = div; + } + } + + var textureUrl = this.baseUrl + responseXML.getElementsByTagName('page')[0].getAttribute('file'); var image = new PIXI.ImageLoader(textureUrl, this.crossorigin); this.texture = image.texture.baseTexture; var data = {}; - var info = this.ajaxRequest.responseXML.getElementsByTagName('info')[0]; - var common = this.ajaxRequest.responseXML.getElementsByTagName('common')[0]; - data.font = info.attributes.getNamedItem('face').nodeValue; - data.size = parseInt(info.attributes.getNamedItem('size').nodeValue, 10); - data.lineHeight = parseInt(common.attributes.getNamedItem('lineHeight').nodeValue, 10); + var info = responseXML.getElementsByTagName('info')[0]; + var common = responseXML.getElementsByTagName('common')[0]; + data.font = info.getAttribute('face'); + data.size = parseInt(info.getAttribute('size'), 10); + data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10); data.chars = {}; //parse letters - var letters = this.ajaxRequest.responseXML.getElementsByTagName('char'); + var letters = responseXML.getElementsByTagName('char'); for (var i = 0; i < letters.length; i++) { - var charCode = parseInt(letters[i].attributes.getNamedItem('id').nodeValue, 10); + var charCode = parseInt(letters[i].getAttribute('id'), 10); var textureRect = new PIXI.Rectangle( - parseInt(letters[i].attributes.getNamedItem('x').nodeValue, 10), - parseInt(letters[i].attributes.getNamedItem('y').nodeValue, 10), - parseInt(letters[i].attributes.getNamedItem('width').nodeValue, 10), - parseInt(letters[i].attributes.getNamedItem('height').nodeValue, 10) + parseInt(letters[i].getAttribute('x'), 10), + parseInt(letters[i].getAttribute('y'), 10), + parseInt(letters[i].getAttribute('width'), 10), + parseInt(letters[i].getAttribute('height'), 10) ); data.chars[charCode] = { - xOffset: parseInt(letters[i].attributes.getNamedItem('xoffset').nodeValue, 10), - yOffset: parseInt(letters[i].attributes.getNamedItem('yoffset').nodeValue, 10), - xAdvance: parseInt(letters[i].attributes.getNamedItem('xadvance').nodeValue, 10), + xOffset: parseInt(letters[i].getAttribute('xoffset'), 10), + yOffset: parseInt(letters[i].getAttribute('yoffset'), 10), + xAdvance: parseInt(letters[i].getAttribute('xadvance'), 10), kerning: {}, texture: PIXI.TextureCache[charCode] = new PIXI.Texture(this.texture, textureRect) @@ -128,12 +140,12 @@ PIXI.BitmapFontLoader.prototype.onXMLLoaded = function() } //parse kernings - var kernings = this.ajaxRequest.responseXML.getElementsByTagName('kerning'); + var kernings = responseXML.getElementsByTagName('kerning'); for (i = 0; i < kernings.length; i++) { - var first = parseInt(kernings[i].attributes.getNamedItem('first').nodeValue, 10); - var second = parseInt(kernings[i].attributes.getNamedItem('second').nodeValue, 10); - var amount = parseInt(kernings[i].attributes.getNamedItem('amount').nodeValue, 10); + var first = parseInt(kernings[i].getAttribute('first'), 10); + var second = parseInt(kernings[i].getAttribute('second'), 10); + var amount = parseInt(kernings[i].getAttribute('amount'), 10); data.chars[second].kerning[first] = amount; diff --git a/src/pixi/loaders/ImageLoader.js b/src/pixi/loaders/ImageLoader.js index c73893f6..9e998b0a 100644 --- a/src/pixi/loaders/ImageLoader.js +++ b/src/pixi/loaders/ImageLoader.js @@ -4,7 +4,7 @@ /** * The image loader class is responsible for loading images file formats ('jpeg', 'jpg', 'png' and 'gif') - * Once the image has been loaded it is stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFromeId() + * Once the image has been loaded it is stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFrameId() * When loaded this class will dispatch a 'loaded' event * * @class ImageLoader @@ -73,7 +73,7 @@ PIXI.ImageLoader.prototype.onLoaded = function() * * * @method loadFramedSpriteSheet - * @param frameWidth {Number} with of each frame + * @param frameWidth {Number} width of each frame * @param frameHeight {Number} height of each frame * @param textureName {String} if given, the frames will be cached in - format */ diff --git a/src/pixi/loaders/JsonLoader.js b/src/pixi/loaders/JsonLoader.js index 127d8cf0..f7d96c3c 100644 --- a/src/pixi/loaders/JsonLoader.js +++ b/src/pixi/loaders/JsonLoader.js @@ -3,9 +3,9 @@ */ /** - * The json file loader is used to load in JSON data and parsing it + * The json file loader is used to load in JSON data and parse it * When loaded this class will dispatch a 'loaded' event - * If load failed this class will dispatch a 'error' event + * If loading fails this class will dispatch an 'error' event * * @class JsonLoader * @uses EventTarget @@ -61,7 +61,7 @@ PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; * @method load */ PIXI.JsonLoader.prototype.load = function () { - this.ajaxRequest = new PIXI.AjaxRequest(); + this.ajaxRequest = new PIXI.AjaxRequest(this.crossorigin); var scope = this; this.ajaxRequest.onreadystatechange = function () { scope.onJSONLoaded(); @@ -105,11 +105,21 @@ PIXI.JsonLoader.prototype.onJSONLoaded = function () { width: rect.w, height: rect.h }); + + // check to see ifthe sprite ha been trimmed.. if (frameData[i].trimmed) { - //var realSize = frameData[i].spriteSourceSize; - PIXI.TextureCache[i].realSize = frameData[i].spriteSourceSize; - PIXI.TextureCache[i].trim.x = 0; // (realSize.x / rect.w) - // calculate the offset! + + var texture = PIXI.TextureCache[i]; + + texture.trimmed = true; + + var actualSize = frameData[i].sourceSize; + var realSize = frameData[i].spriteSourceSize; + + texture.trim.x = realSize.x; + texture.trim.y = realSize.y; + texture.trim.realWidth = actualSize.w; + texture.trim.realHeight = actualSize.h; } } } diff --git a/src/pixi/loaders/SpineLoader.js b/src/pixi/loaders/SpineLoader.js index f0a38ca0..23072f10 100644 --- a/src/pixi/loaders/SpineLoader.js +++ b/src/pixi/loaders/SpineLoader.js @@ -9,7 +9,7 @@ /** * The Spine loader is used to load in JSON spine data - * To generate the data you need to use http://esotericsoftware.com/ and export the "JSON" format + * To generate the data you need to use http://esotericsoftware.com/ and export in the "JSON" format * Due to a clash of names You will need to change the extension of the spine file from *.json to *.anim for it to load * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * You will need to generate a sprite sheet to accompany the spine data @@ -64,26 +64,11 @@ PIXI.SpineLoader.prototype.load = function () { var jsonLoader = new PIXI.JsonLoader(this.url, this.crossorigin); jsonLoader.addEventListener("loaded", function (event) { scope.json = event.content.json; - scope.onJSONLoaded(); + scope.onLoaded(); }); jsonLoader.load(); }; -/** - * Invoke when JSON file is loaded - * - * @method onJSONLoaded - * @private - */ -PIXI.SpineLoader.prototype.onJSONLoaded = function () { - var spineJsonParser = new spine.SkeletonJson(); - var skeletonData = spineJsonParser.readSkeletonData(this.json); - - PIXI.AnimCache[this.url] = skeletonData; - - this.onLoaded(); -}; - /** * Invoke when JSON file is loaded * diff --git a/src/pixi/loaders/SpriteSheetLoader.js b/src/pixi/loaders/SpriteSheetLoader.js index e9844cec..bfb827a7 100644 --- a/src/pixi/loaders/SpriteSheetLoader.js +++ b/src/pixi/loaders/SpriteSheetLoader.js @@ -4,11 +4,11 @@ /** * The sprite sheet loader is used to load in JSON sprite sheet data - * To generate the data you can use http://www.codeandweb.com/texturepacker and publish the 'JSON' format + * To generate the data you can use http://www.codeandweb.com/texturepacker and publish in the 'JSON' format * There is a free version so thats nice, although the paid version is great value for money. - * It is highly recommended to use Sprite sheets (also know as texture atlas') as it means sprite's can be batched and drawn together for highly increased rendering speed. - * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFromeId() - * This loader will also load the image file that the Spritesheet points to as well as the data. + * It is highly recommended to use Sprite sheets (also know as a 'texture atlas') as it means sprites can be batched and drawn together for highly increased rendering speed. + * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFrameId() + * This loader will load the image file that the Spritesheet points to as well as the data. * When loaded this class will dispatch a 'loaded' event * * @class SpriteSheetLoader @@ -80,47 +80,9 @@ PIXI.SpriteSheetLoader.prototype.load = function () { var jsonLoader = new PIXI.JsonLoader(this.url, this.crossorigin); jsonLoader.addEventListener('loaded', function (event) { scope.json = event.content.json; - scope.onJSONLoaded(); - }); - jsonLoader.load(); -}; - -/** - * Invoke when JSON file is loaded - * - * @method onJSONLoaded - * @private - */ -PIXI.SpriteSheetLoader.prototype.onJSONLoaded = function () { - var scope = this; - var textureUrl = this.baseUrl + this.json.meta.image; - var image = new PIXI.ImageLoader(textureUrl, this.crossorigin); - var frameData = this.json.frames; - - this.texture = image.texture.baseTexture; - image.addEventListener('loaded', function () { scope.onLoaded(); }); - - for (var i in frameData) { - var rect = frameData[i].frame; - if (rect) { - PIXI.TextureCache[i] = new PIXI.Texture(this.texture, { - x: rect.x, - y: rect.y, - width: rect.w, - height: rect.h - }); - if (frameData[i].trimmed) { - //var realSize = frameData[i].spriteSourceSize; - PIXI.TextureCache[i].realSize = frameData[i].spriteSourceSize; - PIXI.TextureCache[i].trim.x = 0; // (realSize.x / rect.w) - // calculate the offset! - } - } - } - - image.load(); + jsonLoader.load(); }; /** diff --git a/src/pixi/primitives/Graphics.js b/src/pixi/primitives/Graphics.js index 92dee1e2..7b84678c 100644 --- a/src/pixi/primitives/Graphics.js +++ b/src/pixi/primitives/Graphics.js @@ -5,8 +5,8 @@ /** * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. - * It is important to know that with the webGL renderer only simple polys can be filled at this stage - * Complex polys will not be filled. Heres an example of a complex poly: http://www.goodboydigital.com/wp-content/uploads/2013/06/complexPolygon.png + * It is important to know that with the webGL renderer only simple polygons can be filled at this stage + * Complex polygons will not be filled. Heres an example of a complex polygon: http://www.goodboydigital.com/wp-content/uploads/2013/06/complexPolygon.png * * @class Graphics * @extends DisplayObjectContainer @@ -51,6 +51,25 @@ PIXI.Graphics = function() */ this.graphicsData = []; + + /** + * The tint applied to the graphic shape. This is a hex value + * + * @property tint + * @type Number + * @default 0xFFFFFF + */ + this.tint = 0xFFFFFF;// * Math.random(); + + /** + * The blend mode to be applied to the graphic shape + * + * @property blendMode + * @type Number + * @default PIXI.blendModes.NORMAL; + */ + this.blendMode = PIXI.blendModes.NORMAL; + /** * Current path * @@ -59,6 +78,39 @@ PIXI.Graphics = function() * @private */ this.currentPath = {points:[]}; + + /** + * WebGL lines ? TODO-Alvin + * + * @property _webGL + * @type Array + * @private + */ + this._webGL = []; + + /** + * Whether this shape is used as a mask + * + * @property isMask + * @type isMask + */ + this.isMask = false; + + /** + * The bounds of the graphic shape as rectangle object + * + * @property bounds + * @type Rectangle + */ + this.bounds = null; + + /** + * the bound padding TODO-Alvin + * + * @property bounds + * @type Number + */ + this.boundsPadding = 10; }; // constructor @@ -66,7 +118,39 @@ PIXI.Graphics.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ) PIXI.Graphics.prototype.constructor = PIXI.Graphics; /** - * Specifies a line style used for subsequent calls to Graphics methods such as the lineTo() method or the drawCircle() method. + * If cacheAsBitmap is true the graphics object will then be rendered as if it was a sprite. + * This is useful if your graphics element does not change often as it will speed up the rendering of the object + * It is also usful as the graphics object will always be antialiased because it will be rendered using canvas + * Not recommended if you are constanly redrawing the graphics element. + * + * @property cacheAsBitmap + * @default false + * @type Boolean + * @private + */ +Object.defineProperty(PIXI.Graphics.prototype, "cacheAsBitmap", { + get: function() { + return this._cacheAsBitmap; + }, + set: function(value) { + this._cacheAsBitmap = value; + + if(this._cacheAsBitmap) + { + this._generateCachedSprite(); + } + else + { + this.destroyCachedSprite(); + this.dirty = true; + } + + } +}); + + +/** + * Specifies the line style used for subsequent calls to Graphics methods such as the lineTo() method or the drawCircle() method. * * @method lineStyle * @param lineWidth {Number} width of the line to draw, will update the object's stored style @@ -91,8 +175,8 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) * Moves the current drawing position to (x, y). * * @method moveTo - * @param x {Number} the X coord to move to - * @param y {Number} the Y coord to move to + * @param x {Number} the X coordinate to move to + * @param y {Number} the Y coordinate to move to */ PIXI.Graphics.prototype.moveTo = function(x, y) { @@ -111,8 +195,8 @@ PIXI.Graphics.prototype.moveTo = function(x, y) * the current drawing position is then set to (x, y). * * @method lineTo - * @param x {Number} the X coord to draw to - * @param y {Number} the Y coord to draw to + * @param x {Number} the X coordinate to draw to + * @param y {Number} the Y coordinate to draw to */ PIXI.Graphics.prototype.lineTo = function(x, y) { @@ -125,11 +209,12 @@ PIXI.Graphics.prototype.lineTo = function(x, y) * (such as lineTo() or drawCircle()) use when drawing. * * @method beginFill - * @param color {uint} the color of the fill - * @param alpha {Number} the alpha + * @param color {Number} the color of the fill + * @param alpha {Number} the alpha of the fill */ PIXI.Graphics.prototype.beginFill = function(color, alpha) { + this.filling = true; this.fillColor = color || 0; this.fillAlpha = (arguments.length < 2) ? 1 : alpha; @@ -171,12 +256,13 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) * Draws a circle. * * @method drawCircle - * @param x {Number} The X coord of the center of the circle - * @param y {Number} The Y coord of the center of the circle + * @param x {Number} The X coordinate of the center of the circle + * @param y {Number} The Y coordinate of the center of the circle * @param radius {Number} The radius of the circle */ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { + if (!this.currentPath.points.length) this.graphicsData.pop(); this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, @@ -191,13 +277,14 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) * Draws an ellipse. * * @method drawEllipse - * @param x {Number} - * @param y {Number} - * @param width {Number} - * @param height {Number} + * @param x {Number} The X coordinate of the upper-left corner of the framing rectangle of this ellipse + * @param y {Number} The Y coordinate of the upper-left corner of the framing rectangle of this ellipse + * @param width {Number} The width of the ellipse + * @param height {Number} The height of the ellipse */ PIXI.Graphics.prototype.drawEllipse = function( x, y, width, height) { + if (!this.currentPath.points.length) this.graphicsData.pop(); this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, @@ -225,74 +312,317 @@ PIXI.Graphics.prototype.clear = function() this.bounds = null; //new PIXI.Rectangle(); }; - -PIXI.Graphics.prototype.updateFilterBounds = function() +/** + * Useful function that returns a texture of the graphics object that can then be used to create sprites + * This can be quite useful if your geometry is complicated and needs to be reused multiple times. + * + * @method generateTexture + * @return {Texture} a texture of the graphics object + */ +PIXI.Graphics.prototype.generateTexture = function() { - if(!this.bounds) + var bounds = this.getBounds(); + + var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height); + var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + + canvasBuffer.context.translate(-bounds.x,-bounds.y); + + PIXI.CanvasGraphics.renderGraphics(this, canvasBuffer.context); + + return texture; +}; + +/** +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Graphics.prototype._renderWebGL = function(renderSession) +{ + // if the sprite is not visible or the alpha is 0 then no need to render this element + if(this.visible === false || this.alpha === 0 || this.isMask === true)return; + + if(this._cacheAsBitmap) { - var minX = Infinity; - var maxX = -Infinity; - - var minY = Infinity; - var maxY = -Infinity; - - var points, x, y; - - for (var i = 0; i < this.graphicsData.length; i++) { - var data = this.graphicsData[i]; - var type = data.type; - var lineWidth = data.lineWidth; - - points = data.points; - - if(type === PIXI.Graphics.RECT) - { - x = points.x - lineWidth/2; - y = points.y - lineWidth/2; - var width = points.width + lineWidth; - var height = points.height + lineWidth; - - minX = x < minX ? x : minX; - maxX = x + width > maxX ? x + width : maxX; - - minY = y < minY ? x : minY; - maxY = y + height > maxY ? y + height : maxY; - } - else if(type === PIXI.Graphics.CIRC || type === PIXI.Graphics.ELIP) - { - x = points.x; - y = points.y; - var radius = points.radius + lineWidth/2; - - minX = x - radius < minX ? x - radius : minX; - maxX = x + radius > maxX ? x + radius : maxX; - - minY = y - radius < minY ? y - radius : minY; - maxY = y + radius > maxY ? y + radius : maxY; - } - else - { - // POLY - for (var j = 0; j < points.length; j+=2) - { - - x = points[j]; - y = points[j+1]; - - minX = x-lineWidth < minX ? x-lineWidth : minX; - maxX = x+lineWidth > maxX ? x+lineWidth : maxX; - - minY = y-lineWidth < minY ? y-lineWidth : minY; - maxY = y+lineWidth > maxY ? y+lineWidth : maxY; - } - } + + if(this.dirty) + { + this._generateCachedSprite(); + // we will also need to update the texture on the gpu too! + PIXI.updateWebGLTexture(this._cachedSprite.texture.baseTexture, renderSession.gl); + + this.dirty = false; } - this.bounds = new PIXI.Rectangle(minX, minY, maxX - minX, maxY - minY); + PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + + return; + } + else + { + renderSession.spriteBatch.stop(); + + if(this._mask)renderSession.maskManager.pushMask(this.mask, renderSession); + if(this._filters)renderSession.filterManager.pushFilter(this._filterBlock); + + // check blend mode + if(this.blendMode !== renderSession.spriteBatch.currentBlendMode) + { + renderSession.spriteBatch.currentBlendMode = this.blendMode; + var blendModeWebGL = PIXI.blendModesWebGL[renderSession.spriteBatch.currentBlendMode]; + renderSession.spriteBatch.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + } + + PIXI.WebGLGraphics.renderGraphics(this, renderSession); + + // only render if it has children! + if(this.children.length) + { + renderSession.spriteBatch.start(); + + // simple render children! + for(var i=0, j=this.children.length; i maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + return bounds; +}; + +/** + * Update the bounds of the object + * + * @method updateBounds + */ +PIXI.Graphics.prototype.updateBounds = function() +{ + + var minX = Infinity; + var maxX = -Infinity; + + var minY = Infinity; + var maxY = -Infinity; + + var points, x, y, w, h; + + for (var i = 0; i < this.graphicsData.length; i++) { + var data = this.graphicsData[i]; + var type = data.type; + var lineWidth = data.lineWidth; + + points = data.points; + + if(type === PIXI.Graphics.RECT) + { + x = points[0] - lineWidth/2; + y = points[1] - lineWidth/2; + w = points[2] + lineWidth; + h = points[3] + lineWidth; + + minX = x < minX ? x : minX; + maxX = x + w > maxX ? x + w : maxX; + + minY = y < minY ? x : minY; + maxY = y + h > maxY ? y + h : maxY; + } + else if(type === PIXI.Graphics.CIRC || type === PIXI.Graphics.ELIP) + { + x = points[0]; + y = points[1]; + w = points[2] + lineWidth/2; + h = points[3] + lineWidth/2; + + minX = x - w < minX ? x - w : minX; + maxX = x + w > maxX ? x + w : maxX; + + minY = y - h < minY ? y - h : minY; + maxY = y + h > maxY ? y + h : maxY; + } + else + { + // POLY + for (var j = 0; j < points.length; j+=2) + { + + x = points[j]; + y = points[j+1]; + minX = x-lineWidth < minX ? x-lineWidth : minX; + maxX = x+lineWidth > maxX ? x+lineWidth : maxX; + + minY = y-lineWidth < minY ? y-lineWidth : minY; + maxY = y+lineWidth > maxY ? y+lineWidth : maxY; + } + } + } + + var padding = this.boundsPadding; + this.bounds = new PIXI.Rectangle(minX - padding, minY - padding, (maxX - minX) + padding * 2, (maxY - minY) + padding * 2); +}; + + +/** + * Generates the cached sprite that was made using the generate TODO-Alvin + * + * @method _generateCachedSprite + * @private + */ +PIXI.Graphics.prototype._generateCachedSprite = function() +{ + var bounds = this.getBounds(); + + if(!this._cachedSprite) + { + var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height); + var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + + this._cachedSprite = new PIXI.Sprite(texture); + this._cachedSprite.buffer = canvasBuffer; + + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.buffer.resize(bounds.width, bounds.height); + } + + // leverage the anchor to account for the offset of the element + this._cachedSprite.anchor.x = -( bounds.x / bounds.width ); + this._cachedSprite.anchor.y = -( bounds.y / bounds.height ); + + // this._cachedSprite.buffer.context.save(); + this._cachedSprite.buffer.context.translate(-bounds.x,-bounds.y); + + PIXI.CanvasGraphics.renderGraphics(this, this._cachedSprite.buffer.context); + // this._cachedSprite.buffer.context.restore(); +}; + +PIXI.Graphics.prototype.destroyCachedSprite = function() +{ + this._cachedSprite.texture.destroy(true); + + // let the gc collect the unused sprite + // TODO could be object pooled! + this._cachedSprite = null; +}; + + // SOME TYPES: PIXI.Graphics.POLY = 0; PIXI.Graphics.RECT = 1; diff --git a/src/pixi/renderers/canvas/CanvasGraphics.js b/src/pixi/renderers/canvas/CanvasGraphics.js index a34c4002..cdb4b870 100644 --- a/src/pixi/renderers/canvas/CanvasGraphics.js +++ b/src/pixi/renderers/canvas/CanvasGraphics.js @@ -20,8 +20,8 @@ PIXI.CanvasGraphics = function() * @static * @private * @method renderGraphics - * @param graphics {Graphics} - * @param context {Context2D} + * @param graphics {Graphics} the actual graphics object to render + * @param context {Context2D} the 2d drawing method of the canvas */ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { @@ -154,8 +154,8 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) * @static * @private * @method renderGraphicsMask - * @param graphics {Graphics} - * @param context {Context2D} + * @param graphics {Graphics} the graphics which will be used as a mask + * @param context {Context2D} the context 2d method of the canvas */ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { diff --git a/src/pixi/renderers/canvas/CanvasRenderer.js b/src/pixi/renderers/canvas/CanvasRenderer.js index 8062a6eb..267ad360 100644 --- a/src/pixi/renderers/canvas/CanvasRenderer.js +++ b/src/pixi/renderers/canvas/CanvasRenderer.js @@ -8,14 +8,72 @@ * * @class CanvasRenderer * @constructor - * @param width=0 {Number} the width of the canvas view - * @param height=0 {Number} the height of the canvas view - * @param view {Canvas} the canvas to use as a view, optional - * @param transparent=false {Boolean} the transparency of the render view, default false + * @param width=800 {Number} the width of the canvas view + * @param height=600 {Number} the height of the canvas view + * @param [view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [transparent=false] {Boolean} the transparency of the render view, default false */ PIXI.CanvasRenderer = function(width, height, view, transparent) { - this.transparent = transparent; + PIXI.defaultRenderer = PIXI.defaultRenderer || this; + + this.type = PIXI.CANVAS_RENDERER; + + + /** + * Whether the render view is transparent + * + * @property transparent + * @type Boolean + */ + this.transparent = !!transparent; + + if(!PIXI.blendModesCanvas) + { + PIXI.blendModesCanvas = []; + + if(PIXI.canUseNewCanvasBlendModes()) + { + PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK??? + PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "multiply"; + PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "screen"; + PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY] = "overlay"; + PIXI.blendModesCanvas[PIXI.blendModes.DARKEN] = "darken"; + PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN] = "lighten"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "color-dodge"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "color-burn"; + PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "hard-light"; + PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "soft-light"; + PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "difference"; + PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "exclusion"; + PIXI.blendModesCanvas[PIXI.blendModes.HUE] = "hue"; + PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "saturation"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR] = "color"; + PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "luminosity"; + } + else + { + // this means that the browser does not support the cool new blend modes in canvas "cough" ie "cough" + PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK??? + PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.DARKEN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.HUE] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.COLOR] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "source-over"; + } + } /** * The width of the canvas view @@ -36,33 +94,19 @@ PIXI.CanvasRenderer = function(width, height, view, transparent) this.height = height || 600; /** - * The canvas element that the everything is drawn to + * The canvas element that everything is drawn to * * @property view - * @type Canvas + * @type HTMLCanvasElement */ - this.view = view || document.createElement( 'canvas' ); + this.view = view || document.createElement( "canvas" ); /** - * The canvas context that the everything is drawn to + * The canvas 2d context that everything is drawn with * @property context - * @type Canvas 2d Context + * @type HTMLCanvasElement 2d Context */ - this.context = this.view.getContext( '2d' ); - - //some filter variables - this.smoothProperty = null; - - if('imageSmoothingEnabled' in this.context) - this.smoothProperty = 'imageSmoothingEnabled'; - else if('webkitImageSmoothingEnabled' in this.context) - this.smoothProperty = 'webkitImageSmoothingEnabled'; - else if('mozImageSmoothingEnabled' in this.context) - this.smoothProperty = 'mozImageSmoothingEnabled'; - else if('oImageSmoothingEnabled' in this.context) - this.smoothProperty = 'oImageSmoothingEnabled'; - - this.scaleMode = null; + this.context = this.view.getContext( "2d", { alpha: this.transparent } ); this.refresh = true; // hack to enable some hardware acceleration! @@ -71,6 +115,34 @@ PIXI.CanvasRenderer = function(width, height, view, transparent) this.view.width = this.width; this.view.height = this.height; this.count = 0; + + /** + * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer + * @property CanvasMaskManager + * @type CanvasMaskManager + */ + this.maskManager = new PIXI.CanvasMaskManager(); + + /** + * RenderSession TODO-Alvin + * @property renderSession + * @type Object + */ + this.renderSession = { + context: this.context, + maskManager: this.maskManager, + scaleMode: null, + smoothProperty: null + }; + + if("imageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "imageSmoothingEnabled"; + else if("webkitImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "webkitImageSmoothingEnabled"; + else if("mozImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "mozImageSmoothingEnabled"; + else if("oImageSmoothingEnabled" in this.context) + this.renderSession.smoothProperty = "oImageSmoothingEnabled"; }; // constructor @@ -88,20 +160,34 @@ PIXI.CanvasRenderer.prototype.render = function(stage) //stage.__childrenRemoved = []; // update textures if need be - PIXI.texturesToUpdate = []; - PIXI.texturesToDestroy = []; + PIXI.texturesToUpdate.length = 0; + PIXI.texturesToDestroy.length = 0; - PIXI.visibleCount++; stage.updateTransform(); // update the background color - if(this.view.style.backgroundColor !== stage.backgroundColorString && !this.transparent) - this.view.style.backgroundColor = stage.backgroundColorString; + /* if(this.view.style.backgroundColor !== stage.backgroundColorString && !this.transparent) + this.view.style.backgroundColor = stage.backgroundColorString; */ this.context.setTransform(1,0,0,1,0,0); - this.context.clearRect(0, 0, this.width, this.height); + + if(this.view.style.backgroundColor !== stage.backgroundColorString ) + { + if(!this.transparent) + { + this.context.globalAlpha = 1; + this.context.fillStyle = stage.backgroundColorString; + this.context.fillRect(0, 0, this.width, this.height); + } + else + { + this.context.clearRect(0, 0, this.width, this.height); + } + } + + //console.log(this.view.style.backgroundColor) + this.renderDisplayObject(stage); - //as // run interaction! if(stage.interactive) @@ -117,12 +203,12 @@ PIXI.CanvasRenderer.prototype.render = function(stage) // remove frame updates.. if(PIXI.Texture.frameUpdates.length > 0) { - PIXI.Texture.frameUpdates = []; + PIXI.Texture.frameUpdates.length = 0; } }; /** - * resizes the canvas view to the specified width and height + * Resizes the canvas view to the specified width and height * * @method resize * @param width {Number} the new width of the canvas view @@ -142,119 +228,17 @@ PIXI.CanvasRenderer.prototype.resize = function(width, height) * * @method renderDisplayObject * @param displayObject {DisplayObject} The displayObject to render + * @param context {Context2D} the context 2d method of the canvas * @private */ -PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) +PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context) { - // no loger recurrsive! - var transform; - var context = this.context; + // no longer recursive! + //var transform; + //var context = this.context; - context.globalCompositeOperation = 'source-over'; - - // one the display object hits this. we can break the loop - var testObject = displayObject.last._iNext; - displayObject = displayObject.first; - - do - { - transform = displayObject.worldTransform; - - if(!displayObject.visible) - { - displayObject = displayObject.last._iNext; - continue; - } - - if(!displayObject.renderable) - { - displayObject = displayObject._iNext; - continue; - } - - if(displayObject instanceof PIXI.Sprite) - { - - var frame = displayObject.texture.frame; - - //ignore null sources - if(frame && frame.width && frame.height && displayObject.texture.baseTexture.source) - { - context.globalAlpha = displayObject.worldAlpha; - - context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - - //if smoothingEnabled is supported and we need to change the smoothing property for this texture - if(this.smoothProperty && this.scaleMode !== displayObject.texture.baseTexture.scaleMode) { - this.scaleMode = displayObject.texture.baseTexture.scaleMode; - context[this.smoothProperty] = (this.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR); - } - - context.drawImage(displayObject.texture.baseTexture.source, - frame.x, - frame.y, - frame.width, - frame.height, - (displayObject.anchor.x) * -frame.width, - (displayObject.anchor.y) * -frame.height, - frame.width, - frame.height); - } - } - else if(displayObject instanceof PIXI.Strip) - { - context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - this.renderStrip(displayObject); - } - else if(displayObject instanceof PIXI.TilingSprite) - { - context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - this.renderTilingSprite(displayObject); - } - else if(displayObject instanceof PIXI.CustomRenderable) - { - context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - displayObject.renderCanvas(this); - } - else if(displayObject instanceof PIXI.Graphics) - { - context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - PIXI.CanvasGraphics.renderGraphics(displayObject, context); - } - else if(displayObject instanceof PIXI.FilterBlock) - { - if(displayObject.data instanceof PIXI.Graphics) - { - var mask = displayObject.data; - - if(displayObject.open) - { - context.save(); - - var cacheAlpha = mask.alpha; - var maskTransform = mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]); - - mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(mask, context); - context.clip(); - - mask.worldAlpha = cacheAlpha; - } - else - { - context.restore(); - } - } - } - //count++ - displayObject = displayObject._iNext; - } - while(displayObject !== testObject); + this.renderSession.context = context || this.context; + displayObject._renderCanvas(this.renderSession); }; /** @@ -286,45 +270,11 @@ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) context.lineTo(x2, y2); } - context.fillStyle = '#FF0000'; + context.fillStyle = "#FF0000"; context.fill(); context.closePath(); }; -/** - * Renders a tiling sprite - * - * @method renderTilingSprite - * @param sprite {TilingSprite} The tilingsprite to render - * @private - */ -PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) -{ - var context = this.context; - - context.globalAlpha = sprite.worldAlpha; - - if(!sprite.__tilePattern) - sprite.__tilePattern = context.createPattern(sprite.texture.baseTexture.source, 'repeat'); - - context.beginPath(); - - var tilePosition = sprite.tilePosition; - var tileScale = sprite.tileScale; - - // offset - context.scale(tileScale.x,tileScale.y); - context.translate(tilePosition.x, tilePosition.y); - - context.fillStyle = sprite.__tilePattern; - context.fillRect(-tilePosition.x,-tilePosition.y,sprite.width / tileScale.x, sprite.height / tileScale.y); - - context.scale(1/tileScale.x, 1/tileScale.y); - context.translate(-tilePosition.x, -tilePosition.y); - - context.closePath(); -}; - /** * Renders a strip * @@ -380,3 +330,51 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); } }; + +/** + * Creates a Canvas element of the given size + * + * @method CanvasBuffer + * @param width {Number} the width for the newly created canvas + * @param height {Number} the height for the newly created canvas + * @static + * @private + */ +PIXI.CanvasBuffer = function(width, height) +{ + this.width = width; + this.height = height; + + this.canvas = document.createElement( "canvas" ); + this.context = this.canvas.getContext( "2d" ); + + this.canvas.width = width; + this.canvas.height = height; +}; + +/** + * Clears the canvas that was created by the CanvasBuffer class + * + * @method clear + * @private + */ +PIXI.CanvasBuffer.prototype.clear = function() +{ + this.context.clearRect(0,0, this.width, this.height); +}; + +/** + * Resizes the canvas that was created by the CanvasBuffer class to the specified width and height + * + * @method resize + * @param width {Number} the new width of the canvas + * @param height {Number} the new height of the canvas + * @private + */ + +PIXI.CanvasBuffer.prototype.resize = function(width, height) +{ + this.width = this.canvas.width = width; + this.height = this.canvas.height = height; +}; + diff --git a/src/pixi/renderers/canvas/utils/CanvasMaskManager.js b/src/pixi/renderers/canvas/utils/CanvasMaskManager.js new file mode 100644 index 00000000..e9a9f974 --- /dev/null +++ b/src/pixi/renderers/canvas/utils/CanvasMaskManager.js @@ -0,0 +1,51 @@ +/** + * @author Mat Groves + * + * + */ +/** + * A set of functions used to handle masking + * + * @class CanvasMaskManager + */ +PIXI.CanvasMaskManager = function() +{ + +}; + +/** + * TODO-Alvin + * + * @method pushMask + * @param maskData TODO-Alvin + * @param context {Context2D} the 2d drawing method of the canvas + */ +PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, context) +{ + context.save(); + + //maskData.visible = false; + // maskData.alpha = 0; + + var cacheAlpha = maskData.alpha; + var transform = maskData.worldTransform; + + context.setTransform(transform.a, transform.c, transform.b, transform.d, transform.tx, transform.ty); + + PIXI.CanvasGraphics.renderGraphicsMask(maskData, context); + + context.clip(); + + maskData.worldAlpha = cacheAlpha; +}; + +/** + * Restores the current drawing context to the state it was before the mask was applied + * + * @method popMask + * @param context {Context2D} the 2d drawing method of the canvas + */ +PIXI.CanvasMaskManager.prototype.popMask = function(context) +{ + context.restore(); +}; \ No newline at end of file diff --git a/src/pixi/renderers/canvas/utils/CanvasTinter.js b/src/pixi/renderers/canvas/utils/CanvasTinter.js new file mode 100644 index 00000000..5bb4e3ef --- /dev/null +++ b/src/pixi/renderers/canvas/utils/CanvasTinter.js @@ -0,0 +1,243 @@ + +/** + * @author Mat Groves + * + * + */ + +/** + * @class CanvasTinter + * @constructor + * @static + */ +PIXI.CanvasTinter = function() +{ + /// this.textureCach +}; + +//PIXI.CanvasTinter.cachTint = true; + + +/** + * TODO-Alvin + * @method getTintedTexture + * @param sprite {Sprite} the sprite to tint + * @param color {Number} the color to use to tint the sprite with + */ +PIXI.CanvasTinter.getTintedTexture = function(sprite, color) +{ + // + // cache on sprite + // cache on texture + // no cache + + var texture = sprite.texture; + + color = PIXI.CanvasTinter.roundColor(color); + + var stringColor = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + + texture.tintCache = texture.tintCache || {}; + + if(texture.tintCache[stringColor]) return texture.tintCache[stringColor]; + + // clone texture.. + var canvas = PIXI.CanvasTinter.canvas || document.createElement("canvas"); + + //PIXI.CanvasTinter.tintWithPerPixel(texture, stringColor, canvas); + + + PIXI.CanvasTinter.tintMethod(texture, color, canvas); + + if(PIXI.CanvasTinter.convertTintToImage) + { + // is this better? + var tintImage = new Image(); + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + PIXI.CanvasTinter.canvas = null; + + } + + return canvas; +}; + +/** + * Tint a texture using the "multiply" operation + * @method tintWithMultiply + * @param texture {texture} the texture to tint + * @param color {Number} the color to use to tint the sprite with + * @param canvas {HTMLCanvasElement} the current canvas + */ +PIXI.CanvasTinter.tintWithMultiply = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var frame = texture.frame; + + canvas.width = frame.width; + canvas.height = frame.height; + + context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + + context.fillRect(0, 0, frame.width, frame.height); + + context.globalCompositeOperation = "multiply"; + + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); + + context.globalCompositeOperation = "destination-atop"; + + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); +}; + +/** + * Tint a texture using the "overlay" operation + * @method tintWithOverlay + * @param texture {texture} the texture to tint + * @param color {Number} the color to use to tint the sprite with + * @param canvas {HTMLCanvasElement} the current canvas + */ +PIXI.CanvasTinter.tintWithOverlay = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var frame = texture.frame; + + canvas.width = frame.width; + canvas.height = frame.height; + + + + context.globalCompositeOperation = "copy"; + context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + context.fillRect(0, 0, frame.width, frame.height); + + context.globalCompositeOperation = "destination-atop"; + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); + + + //context.globalCompositeOperation = "copy"; + +}; + +/** + * Tint a texture pixel per pixel + * @method tintPerPixel + * @param texture {texture} the texture to tint + * @param color {Number} the color to use to tint the sprite with + * @param canvas {HTMLCanvasElement} the current canvas + */ +PIXI.CanvasTinter.tintWithPerPixel = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var frame = texture.frame; + + canvas.width = frame.width; + canvas.height = frame.height; + + context.globalCompositeOperation = "copy"; + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); + + var rgbValues = PIXI.hex2rgb(color); + var r = rgbValues[0], g = rgbValues[1], b = rgbValues[2]; + + var pixelData = context.getImageData(0, 0, frame.width, frame.height); + + var pixels = pixelData.data; + + for (var i = 0; i < pixels.length; i += 4) + { + pixels[i+0] *= r; + pixels[i+1] *= g; + pixels[i+2] *= b; + } + + context.putImageData(pixelData, 0, 0); +}; + +/** + * Rounds the specified color according to the PIXI.CanvasTinter.cacheStepsPerColorChannel + * @method roundColor + * @param color {number} the color to round, should be a hex color + */ +PIXI.CanvasTinter.roundColor = function(color) +{ + var step = PIXI.CanvasTinter.cacheStepsPerColorChannel; + + var rgbValues = PIXI.hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return PIXI.rgb2hex(rgbValues); +}; + +/** + * + * Number of steps which will be used as a cap when rounding colors + * + * @property cacheStepsPerColorChannel + * @type Number + */ +PIXI.CanvasTinter.cacheStepsPerColorChannel = 8; +/** + * + * Number of steps which will be used as a cap when rounding colors + * + * @property convertTintToImage + * @type Boolean + */ +PIXI.CanvasTinter.convertTintToImage = false; + +/** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method + * + * @property canUseMultiply + * @type Boolean + */ +PIXI.CanvasTinter.canUseMultiply = PIXI.canUseNewCanvasBlendModes(); + +PIXI.CanvasTinter.tintMethod = PIXI.CanvasTinter.canUseMultiply ? PIXI.CanvasTinter.tintWithMultiply : PIXI.CanvasTinter.tintWithPerPixel; + diff --git a/src/pixi/renderers/webgl/WebGLBatch.js b/src/pixi/renderers/webgl/WebGLBatch.js deleted file mode 100644 index ba719b36..00000000 --- a/src/pixi/renderers/webgl/WebGLBatch.js +++ /dev/null @@ -1,572 +0,0 @@ -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -PIXI._batchs = []; - -/** - * @private - */ -PIXI._getBatch = function(gl) -{ - if(PIXI._batchs.length === 0) - { - return new PIXI.WebGLBatch(gl); - } - else - { - return PIXI._batchs.pop(); - } -}; - -/** - * @private - */ -PIXI._returnBatch = function(batch) -{ - batch.clean(); - PIXI._batchs.push(batch); -}; - -/** - * @private - */ -PIXI._restoreBatchs = function(gl) -{ - for (var i=0; i < PIXI._batchs.length; i++) - { - PIXI._batchs[i].restoreLostContext(gl); - } -}; - -/** - * A WebGLBatch Enables a group of sprites to be drawn using the same settings. - * if a group of sprites all have the same baseTexture and blendMode then they can be grouped into a batch. - * All the sprites in a batch can then be drawn in one go by the GPU which is hugely efficient. ALL sprites - * in the webGL renderer are added to a batch even if the batch only contains one sprite. Batching is handled - * automatically by the webGL renderer. A good tip is: the smaller the number of batchs there are, the faster - * the webGL renderer will run. - * - * @class WebGLBatch - * @constructor - * @param gl {WebGLContext} an instance of the webGL context - */ -PIXI.WebGLBatch = function(gl) -{ - this.gl = gl; - - this.size = 0; - - this.vertexBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.blendMode = PIXI.blendModes.NORMAL; - this.dynamicSize = 1; -}; - -// constructor -PIXI.WebGLBatch.prototype.constructor = PIXI.WebGLBatch; - -/** - * Cleans the batch so that is can be returned to an object pool and reused - * - * @method clean - */ -PIXI.WebGLBatch.prototype.clean = function() -{ - this.verticies = []; - this.uvs = []; - this.indices = []; - this.colors = []; - this.dynamicSize = 1; - this.texture = null; - this.last = null; - this.size = 0; - this.head = null; - this.tail = null; -}; - -/** - * Recreates the buffers in the event of a context loss - * - * @method restoreLostContext - * @param gl {WebGLContext} - */ -PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) -{ - this.gl = gl; - this.vertexBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); -}; - -/** - * inits the batch's texture and blend mode based if the supplied sprite - * - * @method init - * @param sprite {Sprite} the first sprite to be added to the batch. Only sprites with - * the same base texture and blend mode will be allowed to be added to this batch - */ -PIXI.WebGLBatch.prototype.init = function(sprite) -{ - sprite.batch = this; - this.dirty = true; - this.blendMode = sprite.blendMode; - this.texture = sprite.texture.baseTexture; - this.head = sprite; - this.tail = sprite; - this.size = 1; - - this.growBatch(); -}; - -/** - * inserts a sprite before the specified sprite - * - * @method insertBefore - * @param sprite {Sprite} the sprite to be added - * @param nextSprite {nextSprite} the first sprite will be inserted before this sprite - */ -PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) -{ - this.size++; - - sprite.batch = this; - this.dirty = true; - var tempPrev = nextSprite.__prev; - nextSprite.__prev = sprite; - sprite.__next = nextSprite; - - if(tempPrev) - { - sprite.__prev = tempPrev; - tempPrev.__next = sprite; - } - else - { - this.head = sprite; - } -}; - -/** - * inserts a sprite after the specified sprite - * - * @method insertAfter - * @param sprite {Sprite} the sprite to be added - * @param previousSprite {Sprite} the first sprite will be inserted after this sprite - */ -PIXI.WebGLBatch.prototype.insertAfter = function(sprite, previousSprite) -{ - this.size++; - - sprite.batch = this; - this.dirty = true; - - var tempNext = previousSprite.__next; - previousSprite.__next = sprite; - sprite.__prev = previousSprite; - - if(tempNext) - { - sprite.__next = tempNext; - tempNext.__prev = sprite; - } - else - { - this.tail = sprite; - } -}; - -/** - * removes a sprite from the batch - * - * @method remove - * @param sprite {Sprite} the sprite to be removed - */ -PIXI.WebGLBatch.prototype.remove = function(sprite) -{ - this.size--; - - if(this.size === 0) - { - sprite.batch = null; - sprite.__prev = null; - sprite.__next = null; - return; - } - - if(sprite.__prev) - { - sprite.__prev.__next = sprite.__next; - } - else - { - this.head = sprite.__next; - this.head.__prev = null; - } - - if(sprite.__next) - { - sprite.__next.__prev = sprite.__prev; - } - else - { - this.tail = sprite.__prev; - this.tail.__next = null; - } - - sprite.batch = null; - sprite.__next = null; - sprite.__prev = null; - this.dirty = true; -}; - -/** - * Splits the batch into two with the specified sprite being the start of the new batch. - * - * @method split - * @param sprite {Sprite} the sprite that indicates where the batch should be split - * @return {WebGLBatch} the new batch - */ -PIXI.WebGLBatch.prototype.split = function(sprite) -{ - this.dirty = true; - - var batch = new PIXI.WebGLBatch(this.gl); - batch.init(sprite); - batch.texture = this.texture; - batch.tail = this.tail; - - this.tail = sprite.__prev; - this.tail.__next = null; - - sprite.__prev = null; - // return a splite batch! - - // TODO this size is wrong! - // need to recalculate :/ problem with a linked list! - // unless it gets calculated in the "clean"? - - // need to loop through items as there is no way to know the length on a linked list :/ - var tempSize = 0; - while(sprite) - { - tempSize++; - sprite.batch = batch; - sprite = sprite.__next; - } - - batch.size = tempSize; - this.size -= tempSize; - - return batch; -}; - -/** - * Merges two batchs together - * - * @method merge - * @param batch {WebGLBatch} the batch that will be merged - */ -PIXI.WebGLBatch.prototype.merge = function(batch) -{ - this.dirty = true; - - this.tail.__next = batch.head; - batch.head.__prev = this.tail; - - this.size += batch.size; - - this.tail = batch.tail; - - var sprite = batch.head; - while(sprite) - { - sprite.batch = this; - sprite = sprite.__next; - } -}; - -/** - * Grows the size of the batch. As the elements in the batch cannot have a dynamic size this - * function is used to increase the size of the batch. It also creates a little extra room so - * that the batch does not need to be resized every time a sprite is added - * - * @method growBatch - */ -PIXI.WebGLBatch.prototype.growBatch = function() -{ - var gl = this.gl; - if( this.size === 1) - { - this.dynamicSize = 1; - } - else - { - this.dynamicSize = this.size * 1.5; - } - - // grow verts - this.verticies = new Float32Array(this.dynamicSize * 8); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - - this.uvs = new Float32Array( this.dynamicSize * 8 ); - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvs , gl.DYNAMIC_DRAW); - - this.dirtyUVS = true; - - this.colors = new Float32Array( this.dynamicSize * 4 ); - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); - - this.dirtyColors = true; - - this.indices = new Uint16Array(this.dynamicSize * 6); - var length = this.indices.length/6; - - for (var i = 0; i < length; i++) - { - var index2 = i * 6; - var index3 = i * 4; - this.indices[index2 + 0] = index3 + 0; - this.indices[index2 + 1] = index3 + 1; - this.indices[index2 + 2] = index3 + 2; - this.indices[index2 + 3] = index3 + 0; - this.indices[index2 + 4] = index3 + 2; - this.indices[index2 + 5] = index3 + 3; - } - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); -}; - -/** - * Refresh's all the data in the batch and sync's it with the webGL buffers - * - * @method refresh - */ -PIXI.WebGLBatch.prototype.refresh = function() -{ - if (this.dynamicSize < this.size) - { - this.growBatch(); - } - - var indexRun = 0; - var index, colorIndex; - - var displayObject = this.head; - - while(displayObject) - { - index = indexRun * 8; - - var texture = displayObject.texture; - - var frame = texture.frame; - var tw = texture.baseTexture.width; - var th = texture.baseTexture.height; - - this.uvs[index + 0] = frame.x / tw; - this.uvs[index +1] = frame.y / th; - - this.uvs[index +2] = (frame.x + frame.width) / tw; - this.uvs[index +3] = frame.y / th; - - this.uvs[index +4] = (frame.x + frame.width) / tw; - this.uvs[index +5] = (frame.y + frame.height) / th; - - this.uvs[index +6] = frame.x / tw; - this.uvs[index +7] = (frame.y + frame.height) / th; - - displayObject.updateFrame = false; - - colorIndex = indexRun * 4; - this.colors[colorIndex] = this.colors[colorIndex + 1] = this.colors[colorIndex + 2] = this.colors[colorIndex + 3] = displayObject.worldAlpha; - - displayObject = displayObject.__next; - - indexRun++; - } - - this.dirtyUVS = true; - this.dirtyColors = true; -}; - -/** - * Updates all the relevant geometry and uploads the data to the GPU - * - * @method update - */ -PIXI.WebGLBatch.prototype.update = function() -{ - var worldTransform, width, height, aX, aY, w0, w1, h0, h1, index; - - var a, b, c, d, tx, ty; - - var indexRun = 0; - - var displayObject = this.head; - var verticies = this.verticies; - var uvs = this.uvs; - var colors = this.colors; - - while(displayObject) - { - if(displayObject.vcount === PIXI.visibleCount) - { - width = displayObject.texture.frame.width; - height = displayObject.texture.frame.height; - - // TODO trim?? - aX = displayObject.anchor.x;// - displayObject.texture.trim.x - aY = displayObject.anchor.y; //- displayObject.texture.trim.y - w0 = width * (1-aX); - w1 = width * -aX; - - h0 = height * (1-aY); - h1 = height * -aY; - - index = indexRun * 8; - - worldTransform = displayObject.worldTransform; - - a = worldTransform[0]; - b = worldTransform[3]; - c = worldTransform[1]; - d = worldTransform[4]; - tx = worldTransform[2]; - ty = worldTransform[5]; - - verticies[index + 0 ] = a * w1 + c * h1 + tx; - verticies[index + 1 ] = d * h1 + b * w1 + ty; - - verticies[index + 2 ] = a * w0 + c * h1 + tx; - verticies[index + 3 ] = d * h1 + b * w0 + ty; - - verticies[index + 4 ] = a * w0 + c * h0 + tx; - verticies[index + 5 ] = d * h0 + b * w0 + ty; - - verticies[index + 6] = a * w1 + c * h0 + tx; - verticies[index + 7] = d * h0 + b * w1 + ty; - - if(displayObject.updateFrame || displayObject.texture.updateFrame) - { - this.dirtyUVS = true; - - var texture = displayObject.texture; - - var frame = texture.frame; - var tw = texture.baseTexture.width; - var th = texture.baseTexture.height; - - uvs[index + 0] = frame.x / tw; - uvs[index +1] = frame.y / th; - - uvs[index +2] = (frame.x + frame.width) / tw; - uvs[index +3] = frame.y / th; - - uvs[index +4] = (frame.x + frame.width) / tw; - uvs[index +5] = (frame.y + frame.height) / th; - - uvs[index +6] = frame.x / tw; - uvs[index +7] = (frame.y + frame.height) / th; - - displayObject.updateFrame = false; - } - - // TODO this probably could do with some optimisation.... - if(displayObject.cacheAlpha !== displayObject.worldAlpha) - { - displayObject.cacheAlpha = displayObject.worldAlpha; - - var colorIndex = indexRun * 4; - colors[colorIndex] = colors[colorIndex + 1] = colors[colorIndex + 2] = colors[colorIndex + 3] = displayObject.worldAlpha; - this.dirtyColors = true; - } - } - else - { - index = indexRun * 8; - - verticies[index + 0 ] = verticies[index + 1 ] = verticies[index + 2 ] = verticies[index + 3 ] = verticies[index + 4 ] = verticies[index + 5 ] = verticies[index + 6] = verticies[index + 7] = 0; - } - - indexRun++; - displayObject = displayObject.__next; - } -}; - -/** - * Draws the batch to the frame buffer - * - * @method render - */ -PIXI.WebGLBatch.prototype.render = function(start, end) -{ - start = start || 0; - - if(end === undefined) - end = this.size; - - if(this.dirty) - { - this.refresh(); - this.dirty = false; - } - - if (this.size === 0)return; - - this.update(); - var gl = this.gl; - - //TODO optimize this! - - var shaderProgram = PIXI.defaultShader; - - //gl.useProgram(shaderProgram); - - // update the verts.. - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - // ok.. - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies); - gl.vertexAttribPointer(shaderProgram.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - // update the uvs - //var isDefault = (shaderProgram == PIXI.shaderProgram) - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - if(this.dirtyUVS) - { - this.dirtyUVS = false; - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvs); - } - - gl.vertexAttribPointer(shaderProgram.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, this.texture._glTexture); - - // update color! - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - - if(this.dirtyColors) - { - this.dirtyColors = false; - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.colors); - } - - gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - var len = end - start; - - // DRAW THAT this! - gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); -}; diff --git a/src/pixi/renderers/webgl/WebGLRenderGroup.js b/src/pixi/renderers/webgl/WebGLRenderGroup.js deleted file mode 100644 index e3543fe7..00000000 --- a/src/pixi/renderers/webgl/WebGLRenderGroup.js +++ /dev/null @@ -1,1028 +0,0 @@ -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * A WebGLBatch Enables a group of sprites to be drawn using the same settings. - * if a group of sprites all have the same baseTexture and blendMode then they can be - * grouped into a batch. All the sprites in a batch can then be drawn in one go by the - * GPU which is hugely efficient. ALL sprites in the webGL renderer are added to a batch - * even if the batch only contains one sprite. Batching is handled automatically by the - * webGL renderer. A good tip is: the smaller the number of batchs there are, the faster - * the webGL renderer will run. - * - * @class WebGLBatch - * @contructor - * @param gl {WebGLContext} An instance of the webGL context - */ -PIXI.WebGLRenderGroup = function(gl, transparent) -{ - this.gl = gl; - this.root; - - this.backgroundColor; - this.transparent = transparent == undefined ? true : transparent; - - this.batchs = []; - this.toRemove = []; - // console.log(this.transparent) - this.filterManager = new PIXI.WebGLFilterManager(this.transparent); -} - -// constructor -PIXI.WebGLRenderGroup.prototype.constructor = PIXI.WebGLRenderGroup; - -/** - * Add a display object to the webgl renderer - * - * @method setRenderable - * @param displayObject {DisplayObject} - * @private - */ -PIXI.WebGLRenderGroup.prototype.setRenderable = function(displayObject) -{ - // has this changed?? - if(this.root)this.removeDisplayObjectAndChildren(this.root); - - displayObject.worldVisible = displayObject.visible; - - // soooooo // - // to check if any batchs exist already?? - - // TODO what if its already has an object? should remove it - this.root = displayObject; - this.addDisplayObjectAndChildren(displayObject); -} - -/** - * Renders the stage to its webgl view - * - * @method render - * @param projection {Object} - */ -PIXI.WebGLRenderGroup.prototype.render = function(projection, buffer) -{ - PIXI.WebGLRenderer.updateTextures(); - - var gl = this.gl; - gl.uniform2f(PIXI.defaultShader.projectionVector, projection.x, projection.y); - - this.filterManager.begin(projection, buffer); - - - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - // will render all the elements in the group - var renderable; - - for (var i=0; i < this.batchs.length; i++) - { - - renderable = this.batchs[i]; - if(renderable instanceof PIXI.WebGLBatch) - { - this.batchs[i].render(); - continue; - } - - // render special - this.renderSpecial(renderable, projection); - } - -} - -/** - * Renders a specific displayObject - * - * @method renderSpecific - * @param displayObject {DisplayObject} - * @param projection {Object} - * @private - */ -PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projection, buffer) -{ - PIXI.WebGLRenderer.updateTextures(); - var gl = this.gl; - - gl.uniform2f(PIXI.defaultShader.projectionVector, projection.x, projection.y); - - this.filterManager.begin(projection, buffer); - - // to do! - // render part of the scene... - - var startIndex; - var startBatchIndex; - - var endIndex; - var endBatchIndex; - - /* - * LOOK FOR THE NEXT SPRITE - * This part looks for the closest next sprite that can go into a batch - * it keeps looking until it finds a sprite or gets to the end of the display - * scene graph - */ - var nextRenderable = displayObject.first; - while(nextRenderable._iNext) - { - if(nextRenderable.renderable && nextRenderable.__renderGroup)break; - nextRenderable = nextRenderable._iNext; - } - var startBatch = nextRenderable.batch; - //console.log(nextRenderable); - - //console.log(renderable) - if(nextRenderable instanceof PIXI.Sprite) - { - startBatch = nextRenderable.batch; - - var head = startBatch.head; - var next = head; - - // ok now we have the batch.. need to find the start index! - if(head == nextRenderable) - { - startIndex = 0; - } - else - { - startIndex = 1; - - while(head.__next != nextRenderable) - { - startIndex++; - head = head.__next; - } - } - } - else - { - startBatch = nextRenderable; - } - - // Get the LAST renderable object - var lastRenderable = displayObject.last; - while(lastRenderable._iPrev) - { - if(lastRenderable.renderable && lastRenderable.__renderGroup)break; - lastRenderable = lastRenderable._iNext; - } - - if(lastRenderable instanceof PIXI.Sprite) - { - endBatch = lastRenderable.batch; - - var head = endBatch.head; - - if(head == lastRenderable) - { - endIndex = 0; - } - else - { - endIndex = 1; - - while(head.__next != lastRenderable) - { - endIndex++; - head = head.__next; - } - } - } - else - { - endBatch = lastRenderable; - } - - if(startBatch == endBatch) - { - if(startBatch instanceof PIXI.WebGLBatch) - { - startBatch.render(startIndex, endIndex+1); - } - else - { - this.renderSpecial(startBatch, projection); - } - return; - } - - // now we have first and last! - startBatchIndex = this.batchs.indexOf(startBatch); - endBatchIndex = this.batchs.indexOf(endBatch); - - // DO the first batch - if(startBatch instanceof PIXI.WebGLBatch) - { - startBatch.render(startIndex); - } - else - { - this.renderSpecial(startBatch, projection); - } - - // DO the middle batchs.. - for (var i=startBatchIndex+1; i < endBatchIndex; i++) - { - renderable = this.batchs[i]; - - if(renderable instanceof PIXI.WebGLBatch) - { - this.batchs[i].render(); - } - else - { - this.renderSpecial(renderable, projection); - } - } - - // DO the last batch.. - if(endBatch instanceof PIXI.WebGLBatch) - { - endBatch.render(0, endIndex+1); - } - else - { - this.renderSpecial(endBatch, projection); - } -} - -/** - * Renders a specific renderable - * - * @method renderSpecial - * @param renderable {DisplayObject} - * @param projection {Object} - * @private - */ -PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) -{ - - var worldVisible = renderable.vcount === PIXI.visibleCount - - - if(renderable instanceof PIXI.TilingSprite) - { - if(worldVisible)this.renderTilingSprite(renderable, projection); - } - else if(renderable instanceof PIXI.Strip) - { - if(worldVisible)this.renderStrip(renderable, projection); - } - else if(renderable instanceof PIXI.CustomRenderable) - { - if(worldVisible) renderable.renderWebGL(this, projection); - } - else if(renderable instanceof PIXI.Graphics) - { - if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); - } - else if(renderable instanceof PIXI.FilterBlock) - { - this.handleFilterBlock(renderable, projection); - } -} - -flip = false; -var maskStack = []; -var maskPosition = 0; - -//var usedMaskStack = []; - -PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(filterBlock, projection) -{ - /* - * for now only masks are supported.. - */ - var gl = PIXI.gl; - - if(filterBlock.open) - { - if(filterBlock.data instanceof Array) - { - this.filterManager.pushFilter(filterBlock); - // ok so.. - - } - else - { - maskPosition++; - - maskStack.push(filterBlock) - - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - - gl.stencilFunc(gl.ALWAYS,1,1); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); - - PIXI.WebGLGraphics.renderGraphics(filterBlock.data, projection); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL,0,maskStack.length); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - } - else - { - if(filterBlock.data instanceof Array) - { - this.filterManager.popFilter(); - } - else - { - var maskData = maskStack.pop(filterBlock) - - - if(maskData) - { - gl.colorMask(false, false, false, false); - - gl.stencilFunc(gl.ALWAYS,1,1); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); - - PIXI.WebGLGraphics.renderGraphics(maskData.data, projection); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL,0,maskStack.length); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - }; - - gl.disable(gl.STENCIL_TEST); - } - } -} - -/** - * Updates a webgl texture - * - * @method updateTexture - * @param displayObject {DisplayObject} - * @private - */ -PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) -{ - - // TODO definitely can optimse this function.. - - this.removeObject(displayObject); - - /* - * LOOK FOR THE PREVIOUS RENDERABLE - * This part looks for the closest previous sprite that can go into a batch - * It keeps going back until it finds a sprite or the stage - */ - var previousRenderable = displayObject.first; - while(previousRenderable != this.root) - { - previousRenderable = previousRenderable._iPrev; - if(previousRenderable.renderable && previousRenderable.__renderGroup)break; - } - - /* - * LOOK FOR THE NEXT SPRITE - * This part looks for the closest next sprite that can go into a batch - * it keeps looking until it finds a sprite or gets to the end of the display - * scene graph - */ - var nextRenderable = displayObject.last; - while(nextRenderable._iNext) - { - nextRenderable = nextRenderable._iNext; - if(nextRenderable.renderable && nextRenderable.__renderGroup)break; - } - - this.insertObject(displayObject, previousRenderable, nextRenderable); -} - -/** - * Adds filter blocks - * - * @method addFilterBlocks - * @param start {FilterBlock} - * @param end {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) -{ - start.__renderGroup = this; - end.__renderGroup = this; - /* - * LOOK FOR THE PREVIOUS RENDERABLE - * This part looks for the closest previous sprite that can go into a batch - * It keeps going back until it finds a sprite or the stage - */ - var previousRenderable = start; - while(previousRenderable != this.root.first) - { - previousRenderable = previousRenderable._iPrev; - if(previousRenderable.renderable && previousRenderable.__renderGroup)break; - } - this.insertAfter(start, previousRenderable); - - /* - * LOOK FOR THE NEXT SPRITE - * This part looks for the closest next sprite that can go into a batch - * it keeps looking until it finds a sprite or gets to the end of the display - * scene graph - */ - var previousRenderable2 = end; - while(previousRenderable2 != this.root.first) - { - previousRenderable2 = previousRenderable2._iPrev; - if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; - } - this.insertAfter(end, previousRenderable2); -} - -/** - * Remove filter blocks - * - * @method removeFilterBlocks - * @param start {FilterBlock} - * @param end {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.removeFilterBlocks = function(start, end) -{ - this.removeObject(start); - this.removeObject(end); -} - -/** - * Adds a display object and children to the webgl context - * - * @method addDisplayObjectAndChildren - * @param displayObject {DisplayObject} - * @private - */ -PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject) -{ - if(displayObject.__renderGroup)displayObject.__renderGroup.removeDisplayObjectAndChildren(displayObject); - - /* - * LOOK FOR THE PREVIOUS RENDERABLE - * This part looks for the closest previous sprite that can go into a batch - * It keeps going back until it finds a sprite or the stage - */ - - var previousRenderable = displayObject.first; - while(previousRenderable != this.root.first) - { - previousRenderable = previousRenderable._iPrev; - if(previousRenderable.renderable && previousRenderable.__renderGroup)break; - } - - /* - * LOOK FOR THE NEXT SPRITE - * This part looks for the closest next sprite that can go into a batch - * it keeps looking until it finds a sprite or gets to the end of the display - * scene graph - */ - var nextRenderable = displayObject.last; - while(nextRenderable._iNext) - { - nextRenderable = nextRenderable._iNext; - if(nextRenderable.renderable && nextRenderable.__renderGroup)break; - } - - // one the display object hits this. we can break the loop - - var tempObject = displayObject.first; - var testObject = displayObject.last._iNext; - do - { - tempObject.__renderGroup = this; - - if(tempObject.renderable) - { - - this.insertObject(tempObject, previousRenderable, nextRenderable); - previousRenderable = tempObject; - } - - tempObject = tempObject._iNext; - } - while(tempObject != testObject) -} - -/** - * Removes a display object and children to the webgl context - * - * @method removeDisplayObjectAndChildren - * @param displayObject {DisplayObject} - * @private - */ -PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) -{ - if(displayObject.__renderGroup != this)return; - -// var displayObject = displayObject.first; - var lastObject = displayObject.last; - do - { - displayObject.__renderGroup = null; - if(displayObject.renderable)this.removeObject(displayObject); - displayObject = displayObject._iNext; - } - while(displayObject) -} - -/** - * Inserts a displayObject into the linked list - * - * @method insertObject - * @param displayObject {DisplayObject} - * @param previousObject {DisplayObject} - * @param nextObject {DisplayObject} - * @private - */ -PIXI.WebGLRenderGroup.prototype.insertObject = function(displayObject, previousObject, nextObject) -{ - // while looping below THE OBJECT MAY NOT HAVE BEEN ADDED - var previousSprite = previousObject; - var nextSprite = nextObject; - - /* - * so now we have the next renderable and the previous renderable - * - */ - if(displayObject instanceof PIXI.Sprite) - { - var previousBatch - var nextBatch - - if(previousSprite instanceof PIXI.Sprite) - { - previousBatch = previousSprite.batch; - if(previousBatch) - { - if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode) - { - previousBatch.insertAfter(displayObject, previousSprite); - return; - } - } - } - else - { - // TODO reword! - previousBatch = previousSprite; - } - - if(nextSprite) - { - if(nextSprite instanceof PIXI.Sprite) - { - nextBatch = nextSprite.batch; - - //batch may not exist if item was added to the display list but not to the webGL - if(nextBatch) - { - if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode) - { - nextBatch.insertBefore(displayObject, nextSprite); - return; - } - else - { - if(nextBatch == previousBatch) - { - // THERE IS A SPLIT IN THIS BATCH! // - var splitBatch = previousBatch.split(nextSprite); - // COOL! - // add it back into the array - /* - * OOPS! - * seems the new sprite is in the middle of a batch - * lets split it.. - */ - var batch = PIXI.WebGLRenderer.getBatch(); - - var index = this.batchs.indexOf( previousBatch ); - batch.init(displayObject); - this.batchs.splice(index+1, 0, batch, splitBatch); - - return; - } - } - } - } - else - { - // TODO re-word! - - nextBatch = nextSprite; - } - } - - /* - * looks like it does not belong to any batch! - * but is also not intersecting one.. - * time to create anew one! - */ - - var batch = PIXI.WebGLRenderer.getBatch(); - batch.init(displayObject); - - if(previousBatch) // if this is invalid it means - { - var index = this.batchs.indexOf( previousBatch ); - this.batchs.splice(index+1, 0, batch); - } - else - { - this.batchs.push(batch); - } - - return; - } - else if(displayObject instanceof PIXI.TilingSprite) - { - - // add to a batch!! - this.initTilingSprite(displayObject); - // this.batchs.push(displayObject); - - } - else if(displayObject instanceof PIXI.Strip) - { - // add to a batch!! - this.initStrip(displayObject); - // this.batchs.push(displayObject); - } - else if(displayObject)// instanceof PIXI.Graphics) - { - //displayObject.initWebGL(this); - - // add to a batch!! - //this.initStrip(displayObject); - //this.batchs.push(displayObject); - } - - this.insertAfter(displayObject, previousSprite); - - // insert and SPLIT! - -} - -/** - * Inserts a displayObject into the linked list - * - * @method insertAfter - * @param item {DisplayObject} - * @param displayObject {DisplayObject} The object to insert - * @private - */ -PIXI.WebGLRenderGroup.prototype.insertAfter = function(item, displayObject) -{ - if(displayObject instanceof PIXI.Sprite) - { - var previousBatch = displayObject.batch; - - if(previousBatch) - { - // so this object is in a batch! - - // is it not? need to split the batch - if(previousBatch.tail == displayObject) - { - // is it tail? insert in to batchs - var index = this.batchs.indexOf( previousBatch ); - this.batchs.splice(index+1, 0, item); - } - else - { - // TODO MODIFY ADD / REMOVE CHILD TO ACCOUNT FOR FILTERS (also get prev and next) // - - // THERE IS A SPLIT IN THIS BATCH! // - var splitBatch = previousBatch.split(displayObject.__next); - - // COOL! - // add it back into the array - /* - * OOPS! - * seems the new sprite is in the middle of a batch - * lets split it.. - */ - var index = this.batchs.indexOf( previousBatch ); - this.batchs.splice(index+1, 0, item, splitBatch); - } - } - else - { - this.batchs.push(item); - } - } - else - { - var index = this.batchs.indexOf( displayObject ); - this.batchs.splice(index+1, 0, item); - } -} - -/** - * Removes a displayObject from the linked list - * - * @method removeObject - * @param displayObject {DisplayObject} The object to remove - * @private - */ -PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) -{ - // loop through children.. - // display object // - - // add a child from the render group.. - // remove it and all its children! - //displayObject.cacheVisible = false;//displayObject.visible; - - /* - * removing is a lot quicker.. - * - */ - var batchToRemove; - - if(displayObject instanceof PIXI.Sprite) - { - // should always have a batch! - var batch = displayObject.batch; - if(!batch)return; // this means the display list has been altered befre rendering - - batch.remove(displayObject); - - if(batch.size==0) - { - batchToRemove = batch; - } - } - else - { - batchToRemove = displayObject; - } - - /* - * Looks like there is somthing that needs removing! - */ - if(batchToRemove) - { - var index = this.batchs.indexOf( batchToRemove ); - if(index == -1)return;// this means it was added then removed before rendered - - // ok so.. check to see if you adjacent batchs should be joined. - // TODO may optimise? - if(index == 0 || index == this.batchs.length-1) - { - // wha - eva! just get of the empty batch! - this.batchs.splice(index, 1); - if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); - - return; - } - - if(this.batchs[index-1] instanceof PIXI.WebGLBatch && this.batchs[index+1] instanceof PIXI.WebGLBatch) - { - if(this.batchs[index-1].texture == this.batchs[index+1].texture && this.batchs[index-1].blendMode == this.batchs[index+1].blendMode) - { - //console.log("MERGE") - this.batchs[index-1].merge(this.batchs[index+1]); - - if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); - PIXI.WebGLRenderer.returnBatch(this.batchs[index+1]); - this.batchs.splice(index, 2); - return; - } - } - - this.batchs.splice(index, 1); - if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove); - } -} - - -/** - * Initializes a tiling sprite - * - * @method initTilingSprite - * @param sprite {TilingSprite} The tiling sprite to initialize - * @private - */ -PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) -{ - var gl = this.gl; - - // make the texture tilable.. - - sprite.verticies = new Float32Array([0, 0, - sprite.width, 0, - sprite.width, sprite.height, - 0, sprite.height]); - - sprite.uvs = new Float32Array([0, 0, - 1, 0, - 1, 1, - 0, 1]); - - sprite.colors = new Float32Array([1,1,1,1]); - - sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]); - - sprite._vertexBuffer = gl.createBuffer(); - sprite._indexBuffer = gl.createBuffer(); - sprite._uvBuffer = gl.createBuffer(); - sprite._colorBuffer = gl.createBuffer(); - - gl.bindBuffer(gl.ARRAY_BUFFER, sprite._vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, sprite.verticies, gl.STATIC_DRAW); - - gl.bindBuffer(gl.ARRAY_BUFFER, sprite._uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, sprite.uvs, gl.DYNAMIC_DRAW); - - gl.bindBuffer(gl.ARRAY_BUFFER, sprite._colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, sprite.colors, gl.STATIC_DRAW); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sprite._indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, sprite.indices, gl.STATIC_DRAW); - -// return ( (x > 0) && ((x & (x - 1)) == 0) ); - - if(sprite.texture.baseTexture._glTexture) - { - gl.bindTexture(gl.TEXTURE_2D, sprite.texture.baseTexture._glTexture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); - sprite.texture.baseTexture._powerOf2 = true; - } - else - { - sprite.texture.baseTexture._powerOf2 = true; - } -} - -/** - * Renders a Strip - * - * @method renderStrip - * @param strip {Strip} The strip to render - * @param projection {Object} - * @private - */ -PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) -{ - var gl = this.gl; - - PIXI.activateStripShader(); - - var shader = PIXI.stripShader; - - var program = shader.program; - - var m = PIXI.mat3.clone(strip.worldTransform); - - PIXI.mat3.transpose(m); - -// console.log(projection) - // set the matrix transform for the - gl.uniformMatrix3fv(shader.translationMatrix, false, m); - gl.uniform2f(shader.projectionVector, projection.x, projection.y); - gl.uniform2f(shader.offsetVector, -PIXI.offset.x, -PIXI.offset.y); - - gl.uniform1f(shader.alpha, strip.worldAlpha); - - /* - if(strip.blendMode == PIXI.blendModes.NORMAL) - { - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - } - else - { - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); - } - */ - - //console.log("!!") - if(!strip.dirty) - { - gl.bindBuffer(gl.ARRAY_BUFFER, strip._vertexBuffer); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, strip.verticies) - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - // update the uvs - gl.bindBuffer(gl.ARRAY_BUFFER, strip._uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, strip.texture.baseTexture._glTexture); - - gl.bindBuffer(gl.ARRAY_BUFFER, strip._colorBuffer); - gl.vertexAttribPointer(shader.colorAttribute, 1, gl.FLOAT, false, 0, 0); - - // dont need to upload! - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - } - else - { - strip.dirty = false; - gl.bindBuffer(gl.ARRAY_BUFFER, strip._vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, strip.verticies, gl.STATIC_DRAW) - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - // update the uvs - gl.bindBuffer(gl.ARRAY_BUFFER, strip._uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, strip.uvs, gl.STATIC_DRAW) - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, strip.texture.baseTexture._glTexture); - // console.log(strip.texture.baseTexture._glTexture) - gl.bindBuffer(gl.ARRAY_BUFFER, strip._colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, strip.colors, gl.STATIC_DRAW) - gl.vertexAttribPointer(shader.colorAttribute, 1, gl.FLOAT, false, 0, 0); - - // dont need to upload! - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); - - } - - gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - - PIXI.deactivateStripShader(); - //gl.useProgram(PIXI.currentProgram); -} - -/** - * Renders a TilingSprite - * - * @method renderTilingSprite - * @param sprite {TilingSprite} The tiling sprite to render - * @param projectionMatrix {Object} - * @private - */ -PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix) -{ - var gl = this.gl; - - - var shaderProgram = PIXI.shaderProgram; - - var tilePosition = sprite.tilePosition; - var tileScale = sprite.tileScale; - - var offsetX = tilePosition.x/sprite.texture.baseTexture.width; - var offsetY = tilePosition.y/sprite.texture.baseTexture.height; - - var scaleX = (sprite.width / sprite.texture.baseTexture.width) / tileScale.x; - var scaleY = (sprite.height / sprite.texture.baseTexture.height) / tileScale.y; - - sprite.uvs[0] = 0 - offsetX; - sprite.uvs[1] = 0 - offsetY; - - sprite.uvs[2] = (1 * scaleX) -offsetX; - sprite.uvs[3] = 0 - offsetY; - - sprite.uvs[4] = (1 *scaleX) - offsetX; - sprite.uvs[5] = (1 *scaleY) - offsetY; - - sprite.uvs[6] = 0 - offsetX; - sprite.uvs[7] = (1 *scaleY) - offsetY; - - gl.bindBuffer(gl.ARRAY_BUFFER, sprite._uvBuffer); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, sprite.uvs) - - this.renderStrip(sprite, projectionMatrix); -} - -/** - * Initializes a strip to be rendered - * - * @method initStrip - * @param strip {Strip} The strip to initialize - * @private - */ -PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) -{ - // build the strip! - var gl = this.gl; - var shaderProgram = this.shaderProgram; - - strip._vertexBuffer = gl.createBuffer(); - strip._indexBuffer = gl.createBuffer(); - strip._uvBuffer = gl.createBuffer(); - strip._colorBuffer = gl.createBuffer(); - - gl.bindBuffer(gl.ARRAY_BUFFER, strip._vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, strip.verticies, gl.DYNAMIC_DRAW); - - gl.bindBuffer(gl.ARRAY_BUFFER, strip._uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, strip.uvs, gl.STATIC_DRAW); - - gl.bindBuffer(gl.ARRAY_BUFFER, strip._colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, strip.colors, gl.STATIC_DRAW); - - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); -} - diff --git a/src/pixi/renderers/webgl/WebGLRenderer.js b/src/pixi/renderers/webgl/WebGLRenderer.js index 4d83833b..3edbd33a 100644 --- a/src/pixi/renderers/webgl/WebGLRenderer.js +++ b/src/pixi/renderers/webgl/WebGLRenderer.js @@ -2,15 +2,11 @@ * @author Mat Groves http://matgroves.com/ @Doormat23 */ -PIXI._defaultFrame = new PIXI.Rectangle(0,0,1,1); - -// an instance of the gl context.. -// only one at the moment :/ -PIXI.gl = null; +PIXI.glContexts = []; // this is where we store the webGL contexts for easy access. /** - * the WebGLRenderer is draws the stage and all its content onto a webGL enabled canvas. This renderer - * should be used for browsers support webGL. This Render works by automatically managing webGLBatchs. + * the WebGLRenderer draws the stage 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 webGLBatch's. * So no need for Sprite Batch's or Sprite Cloud's * Dont forget to add the view to your DOM or you will not see anything :) * @@ -18,123 +14,150 @@ PIXI.gl = null; * @constructor * @param width=0 {Number} the width of the canvas view * @param height=0 {Number} the height of the canvas view - * @param view {Canvas} the canvas to use as a view, optional - * @param transparent=false {Boolean} the transparency of the render view, default false + * @param view {HTMLCanvasElement} the canvas to use as a view, optional + * @param transparent=false {Boolean} If the render view is transparent, default false * @param antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { - // do a catch.. only 1 webGL renderer.. + if(!PIXI.defaultRenderer)PIXI.defaultRenderer = this; + this.type = PIXI.WEBGL_RENDERER; + + // do a catch.. only 1 webGL renderer.. + /** + * Whether the render view is transparent + * + * @property transparent + * @type Boolean + */ this.transparent = !!transparent; + /** + * The width of the canvas view + * + * @property width + * @type Number + * @default 800 + */ this.width = width || 800; + + /** + * The height of the canvas view + * + * @property height + * @type Number + * @default 600 + */ this.height = height || 600; + /** + * The canvas element that everything is drawn to + * + * @property view + * @type HTMLCanvasElement + */ this.view = view || document.createElement( 'canvas' ); this.view.width = this.width; this.view.height = this.height; // deal with losing context.. - var scope = this; - this.view.addEventListener('webglcontextlost', function(event) { scope.handleContextLost(event); }, false); - this.view.addEventListener('webglcontextrestored', function(event) { scope.handleContextRestored(event); }, false); + // TODO-Alvin + this.contextLost = this.handleContextLost.bind(this); + this.contextRestoredLost = this.handleContextRestored.bind(this); + // console.log(this.handleContextRestored) + this.view.addEventListener('webglcontextlost', this.contextLost, false); + this.view.addEventListener('webglcontextrestored', this.contextRestoredLost, false); - this.batchs = []; - - var options = { + this.options = { alpha: this.transparent, antialias:!!antialias, // SPEED UP?? - premultipliedAlpha:false, + premultipliedAlpha:!!transparent, stencil:true }; //try 'experimental-webgl' try { - PIXI.gl = this.gl = this.view.getContext('experimental-webgl', options); + this.gl = this.view.getContext('experimental-webgl', this.options); } catch (e) { //try 'webgl' try { - PIXI.gl = this.gl = this.view.getContext('webgl', options); + this.gl = this.view.getContext('webgl', this.options); } catch (e2) { // fail, not able to get a context throw new Error(' This browser does not support webGL. Try using the canvas renderer' + this); } } - PIXI.initDefaultShaders(); - - - - - // PIXI.activateDefaultShader(); - var gl = this.gl; + this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId ++; - gl.useProgram(PIXI.defaultShader.program); + PIXI.glContexts[this.glContextId] = gl; + + if(!PIXI.blendModesWebGL) + { + PIXI.blendModesWebGL = []; + + PIXI.blendModesWebGL[PIXI.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + PIXI.blendModesWebGL[PIXI.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + } - PIXI.WebGLRenderer.gl = gl; - this.batch = new PIXI.WebGLBatch(gl); + + this.projection = new PIXI.Point(); + this.projection.x = this.width/2; + this.projection.y = -this.height/2; + + this.offset = new PIXI.Point(0, 0); + + this.resize(this.width, this.height); + this.contextLost = false; + + // time to create the render managers! each one focuses on managine a state in webGL + this.shaderManager = new PIXI.WebGLShaderManager(gl); // deals with managing the shader programs and their attribs + this.spriteBatch = new PIXI.WebGLSpriteBatch(gl); // manages the rendering of sprites + this.maskManager = new PIXI.WebGLMaskManager(gl); // manages the masks using the stencil buffer + this.filterManager = new PIXI.WebGLFilterManager(gl, this.transparent); // manages the filters + + // + 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.spriteBatch = this.spriteBatch; + + + gl.useProgram(this.shaderManager.defaultShader.program); + gl.disable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); gl.colorMask(true, true, true, this.transparent); - - PIXI.projection = new PIXI.Point(400, 300); - PIXI.offset = new PIXI.Point(0, 0); - - // TODO remove thease globals.. - - this.resize(this.width, this.height); - this.contextLost = false; - - //PIXI.pushShader(PIXI.defaultShader); - - this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl, this.transparent); - // this.stageRenderGroup. = this.transparent }; // constructor PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; -/** - * Gets a new WebGLBatch from the pool - * - * @static - * @method getBatch - * @return {WebGLBatch} - * @private - */ -PIXI.WebGLRenderer.getBatch = function() -{ - if(PIXI._batchs.length === 0) - { - return new PIXI.WebGLBatch(PIXI.WebGLRenderer.gl); - } - else - { - return PIXI._batchs.pop(); - } -}; - -/** - * Puts a batch back into the pool - * - * @static - * @method returnBatch - * @param batch {WebGLBatch} The batch to return - * @private - */ -PIXI.WebGLRenderer.returnBatch = function(batch) -{ - batch.clean(); - PIXI._batchs.push(batch); -}; - /** * Renders the stage to its webGL view * @@ -146,44 +169,49 @@ PIXI.WebGLRenderer.prototype.render = function(stage) if(this.contextLost)return; - // if rendering a new stage clear the batchs.. + // 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; - this.stageRenderGroup.setRenderable(stage); } - // update any textures + // update any textures this includes uvs and uploading them to the gpu PIXI.WebGLRenderer.updateTextures(); // update the scene graph - PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; // -- Does this need to be set every frame? -- // - gl.colorMask(true, true, true, this.transparent); + //gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); + // make sure we are bound to the main frame buffer gl.bindFramebuffer(gl.FRAMEBUFFER, null); - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); + if(this.transparent) + { + gl.clearColor(0, 0, 0, 0); + } + else + { + gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); + } + + gl.clear(gl.COLOR_BUFFER_BIT); - // HACK TO TEST + // this.projection.x = this.width/2; + //this.projection.y = -this.height/2; - this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; - - PIXI.projection.x = this.width/2; - PIXI.projection.y = -this.height/2; - - this.stageRenderGroup.render(PIXI.projection); + this.renderDisplayObject( stage, this.projection ); // interaction - // run interaction! if(stage.interactive) { //need to add some events! @@ -193,17 +221,65 @@ PIXI.WebGLRenderer.prototype.render = function(stage) stage.interactionManager.setTarget(this); } } - - // after rendering lets confirm all frames that have been uodated.. - if(PIXI.Texture.frameUpdates.length > 0) + else { - for (var i=0; i < PIXI.Texture.frameUpdates.length; i++) + if(stage._interactiveEventsAdded) { - PIXI.Texture.frameUpdates[i].updateFrame = false; + stage._interactiveEventsAdded = false; + stage.interactionManager.setTarget(this); } - - PIXI.Texture.frameUpdates = []; } + + /* + //can simulate context loss in Chrome like so: + this.view.onmousedown = function(ev) { + console.dir(this.gl.getSupportedExtensions()); + var ext = ( + gl.getExtension("WEBGL_scompressed_texture_s3tc") + // gl.getExtension("WEBGL_compressed_texture_s3tc") || + // gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || + // gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc") + ); + console.dir(ext); + var loseCtx = this.gl.getExtension("WEBGL_lose_context"); + console.log("killing context"); + loseCtx.loseContext(); + setTimeout(function() { + console.log("restoring context..."); + loseCtx.restoreContext(); + }.bind(this), 1000); + }.bind(this); + */ +}; + +/** + * Renders a display Object + * + * @method renderDIsplayObject + * @param displayObject {DisplayObject} The DisplayObject to render + * @param projection {Point} + * @param buffer {Array} buffer TODO-Alvin + */ +PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, projection, buffer) +{ + // reset the render session data.. + this.renderSession.drawCount = 0; + this.renderSession.currentBlendMode = 9999; + + this.renderSession.projection = projection; + this.renderSession.offset = this.offset; + + // start the sprite batch + this.spriteBatch.begin(this.renderSession); + + // start the filter manager + this.filterManager.begin(this.renderSession, buffer); + + // render the scene! + displayObject._renderWebGL(this.renderSession); + + // finish the sprite batch + this.spriteBatch.end(); }; /** @@ -218,58 +294,19 @@ PIXI.WebGLRenderer.updateTextures = function() var i = 0; //TODO break this out into a texture manager... - for (i = 0; i < PIXI.texturesToUpdate.length; i++) - PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + //for (i = 0; i < PIXI.texturesToUpdate.length; i++) + // PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + + + for (i=0; i < PIXI.Texture.frameUpdates.length; i++) + PIXI.WebGLRenderer.updateTextureFrame(PIXI.Texture.frameUpdates[i]); for (i = 0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); - PIXI.texturesToUpdate = []; - PIXI.texturesToDestroy = []; -}; - -/** - * Updates a loaded webgl texture - * - * @static - * @method updateTexture - * @param texture {Texture} The texture to update - * @private - */ -PIXI.WebGLRenderer.updateTexture = function(texture) -{ - //TODO break this out into a texture manager... - var gl = PIXI.gl; - - if(!texture._glTexture) - { - texture._glTexture = gl.createTexture(); - } - - if(texture.hasLoaded) - { - gl.bindTexture(gl.TEXTURE_2D, texture._glTexture); - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); - - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR ? gl.LINEAR : gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR ? gl.LINEAR : gl.NEAREST); - - // reguler... - - if(!texture._powerOf2) - { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - } - else - { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); - } - - gl.bindTexture(gl.TEXTURE_2D, null); - } + PIXI.texturesToUpdate.length = 0; + PIXI.texturesToDestroy.length = 0; + PIXI.Texture.frameUpdates.length = 0; }; /** @@ -282,13 +319,35 @@ PIXI.WebGLRenderer.updateTexture = function(texture) PIXI.WebGLRenderer.destroyTexture = function(texture) { //TODO break this out into a texture manager... - var gl = PIXI.gl; - if(texture._glTexture) + for (var i = texture._glTextures.length - 1; i >= 0; i--) { - texture._glTexture = gl.createTexture(); - gl.deleteTexture(gl.TEXTURE_2D, texture._glTexture); + var glTexture = texture._glTextures[i]; + var gl = PIXI.glContexts[i]; + + if(gl && glTexture) + { + gl.deleteTexture(glTexture); + } } + + texture._glTextures.length = 0; +}; + +/** + * TODO-Alvin + * + * @method updateTextureFrame + * @param texture {Texture} The texture to update the frame from + * @private + */ +PIXI.WebGLRenderer.updateTextureFrame = function(texture) +{ + texture.updateFrame = false; + + // now set the uvs. Figured that the uv data sits with a texture rather than a sprite. + // so uv data is stored on the texture itself + texture._updateWebGLuvs(); }; /** @@ -308,18 +367,87 @@ PIXI.WebGLRenderer.prototype.resize = function(width, height) this.gl.viewport(0, 0, this.width, this.height); - //var projectionMatrix = this.projectionMatrix; + this.projection.x = this.width/2; + this.projection.y = -this.height/2; +}; - PIXI.projection.x = this.width/2; - PIXI.projection.y = -this.height/2; +/** + * Creates a WebGL texture + * + * @method createWebGLTexture + * @param texture {Texture} the texture to render + * @param gl {webglContext} the WebGL context + * @static + */ +PIXI.createWebGLTexture = function(texture, gl) +{ - //PIXI.size.x = this.width/2; - //PIXI.size.y = -this.height/2; -// projectionMatrix[0] = 2/this.width; -// projectionMatrix[5] = -2/this.height; -// projectionMatrix[12] = -1; -// projectionMatrix[13] = 1; + if(texture.hasLoaded) + { + texture._glTextures[gl.id] = gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + + // reguler... + + if(!texture._powerOf2) + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } + else + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + } + + gl.bindTexture(gl.TEXTURE_2D, null); + } + + return texture._glTextures[gl.id]; +}; + +/** + * Updates a WebGL texture + * + * @method updateWebGLTexture + * @param texture {Texture} the texture to update + * @param gl {webglContext} the WebGL context + * @private + */ +PIXI.updateWebGLTexture = function(texture, gl) +{ + if( texture._glTextures[gl.id] ) + { + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); + + // reguler... + + if(!texture._powerOf2) + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } + else + { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + } + + gl.bindTexture(gl.TEXTURE_2D, null); + } + }; /** @@ -344,26 +472,91 @@ PIXI.WebGLRenderer.prototype.handleContextLost = function(event) */ PIXI.WebGLRenderer.prototype.handleContextRestored = function() { - this.gl = this.view.getContext('experimental-webgl', { - alpha: true - }); - this.initShaders(); + //try 'experimental-webgl' + try { + this.gl = this.view.getContext('experimental-webgl', this.options); + } catch (e) { + //try 'webgl' + try { + this.gl = this.view.getContext('webgl', this.options); + } catch (e2) { + // fail, not able to get a context + throw new Error(' This browser does not support webGL. Try using the canvas renderer' + this); + } + } + + var gl = this.gl; + gl.id = PIXI.WebGLRenderer.glContextId ++; + + + + // need to set the context... + this.shaderManager.setContext(gl); + this.spriteBatch.setContext(gl); + this.maskManager.setContext(gl); + this.filterManager.setContext(gl); + + + this.renderSession.gl = this.gl; + + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + + gl.enable(gl.BLEND); + gl.colorMask(true, true, true, this.transparent); + + this.gl.viewport(0, 0, this.width, this.height); for(var key in PIXI.TextureCache) { var texture = PIXI.TextureCache[key].baseTexture; - texture._glTexture = null; - PIXI.WebGLRenderer.updateTexture(texture); + texture._glTextures = []; } - for (var i=0; i < this.batchs.length; i++) - { - this.batchs[i].restoreLostContext(this.gl); - this.batchs[i].dirty = true; - } - - PIXI._restoreBatchs(this.gl); - + /** + * Whether the context was lost + * @property contextLost + * @type Boolean + */ this.contextLost = false; + }; + +/** + * Destroy TODO-Alvin + * + * @method destroy + */ +PIXI.WebGLRenderer.prototype.destroy = function() +{ + + // deal with losing context.. + + // remove listeners + this.view.removeEventListener('webglcontextlost', this.contextLost); + this.view.removeEventListener('webglcontextrestored', this.contextRestoredLost); + + PIXI.glContexts[this.glContextId] = null; + + this.projection = null; + this.offset = null; + + // time to create the render managers! each one focuses on managine a state in webGL + this.shaderManager.destroy(); + this.spriteBatch.destroy(); + this.maskManager.destroy(); + this.filterManager.destroy(); + + this.shaderManager = null; + this.spriteBatch = null; + this.maskManager = null; + this.filterManager = null; + + this.gl = null; + // + this.renderSession = null; +}; + + +PIXI.WebGLRenderer.glContextId = 0; diff --git a/src/pixi/renderers/webgl/WebGLShaders.js b/src/pixi/renderers/webgl/WebGLShaders.js deleted file mode 100644 index b6b66b7e..00000000 --- a/src/pixi/renderers/webgl/WebGLShaders.js +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -PIXI.initDefaultShaders = function() -{ - PIXI.primitiveShader = new PIXI.PrimitiveShader(); - PIXI.primitiveShader.init(); - - PIXI.stripShader = new PIXI.StripShader(); - PIXI.stripShader.init(); - - PIXI.defaultShader = new PIXI.PixiShader(); - PIXI.defaultShader.init(); - - var gl = PIXI.gl; - var shaderProgram = PIXI.defaultShader.program; - - gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(PIXI.defaultShader.aVertexPosition); - gl.enableVertexAttribArray(PIXI.defaultShader.colorAttribute); - gl.enableVertexAttribArray(PIXI.defaultShader.aTextureCoord); -}; - -PIXI.activatePrimitiveShader = function() -{ - var gl = PIXI.gl; - - gl.useProgram(PIXI.primitiveShader.program); - - gl.disableVertexAttribArray(PIXI.defaultShader.aVertexPosition); - gl.disableVertexAttribArray(PIXI.defaultShader.colorAttribute); - gl.disableVertexAttribArray(PIXI.defaultShader.aTextureCoord); - - gl.enableVertexAttribArray(PIXI.primitiveShader.aVertexPosition); - gl.enableVertexAttribArray(PIXI.primitiveShader.colorAttribute); -}; - -PIXI.deactivatePrimitiveShader = function() -{ - var gl = PIXI.gl; - - gl.useProgram(PIXI.defaultShader.program); - - gl.disableVertexAttribArray(PIXI.primitiveShader.aVertexPosition); - gl.disableVertexAttribArray(PIXI.primitiveShader.colorAttribute); - - gl.enableVertexAttribArray(PIXI.defaultShader.aVertexPosition); - gl.enableVertexAttribArray(PIXI.defaultShader.colorAttribute); - gl.enableVertexAttribArray(PIXI.defaultShader.aTextureCoord); -}; - -PIXI.activateStripShader = function() -{ - var gl = PIXI.gl; - - gl.useProgram(PIXI.stripShader.program); - // gl.disableVertexAttribArray(PIXI.defaultShader.aTextureCoord); -}; - -PIXI.deactivateStripShader = function() -{ - var gl = PIXI.gl; - - gl.useProgram(PIXI.defaultShader.program); - //gl.enableVertexAttribArray(PIXI.defaultShader.aTextureCoord); -}; - -/* - -SHADER COMPILER HELPERS -*/ - -PIXI.CompileVertexShader = function(gl, shaderSrc) -{ - return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); -}; - -PIXI.CompileFragmentShader = function(gl, shaderSrc) -{ - return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); -}; - -PIXI._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; -}; - -PIXI.compileProgram = function(vertexSrc, fragmentSrc) -{ - var gl = PIXI.gl; - var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc); - var vertexShader = PIXI.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/pixi/renderers/webgl/shaders/PixiFastShader.js b/src/pixi/renderers/webgl/shaders/PixiFastShader.js new file mode 100644 index 00000000..94a4adeb --- /dev/null +++ b/src/pixi/renderers/webgl/shaders/PixiFastShader.js @@ -0,0 +1,146 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Richard Davey http://www.photonstorm.com @photonstorm + */ + +/** +* @class PIXI.PixiFastShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.PixiFastShader = function(gl) +{ + + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * @property {any} program - The WebGL program. + */ + this.program = null; + + /** + * @property {array} fragmentSrc - The fragment shader. + */ + this.fragmentSrc = [ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying float vColor;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ]; + + /** + * @property {array} vertexSrc - The vertex shader + */ + 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;', + '}' + ]; + + + /** + * @property {number} textureCount - A local texture counter for multi-texture shaders. + */ + this.textureCount = 0; + + + this.init(); +}; + +/** +* Initialises the shader +* @method init +* +*/ +PIXI.PixiFastShader.prototype.init = function() +{ + + var gl = this.gl; + + var program = PIXI.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 +* @method destroy +* +*/ +PIXI.PixiFastShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attributes = null; +}; diff --git a/src/pixi/renderers/webgl/PixiShader.js b/src/pixi/renderers/webgl/shaders/PixiShader.js similarity index 61% rename from src/pixi/renderers/webgl/PixiShader.js rename to src/pixi/renderers/webgl/shaders/PixiShader.js index 01721c07..08ecd48d 100644 --- a/src/pixi/renderers/webgl/PixiShader.js +++ b/src/pixi/renderers/webgl/shaders/PixiShader.js @@ -7,8 +7,14 @@ * @class PIXI.PixiShader * @constructor */ -PIXI.PixiShader = function() +PIXI.PixiShader = function(gl) { + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + /** * @property {any} program - The WebGL program. */ @@ -20,28 +26,36 @@ PIXI.PixiShader = function() this.fragmentSrc = [ 'precision lowp float;', 'varying vec2 vTextureCoord;', - 'varying float vColor;', + 'varying vec4 vColor;', 'uniform sampler2D uSampler;', 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', '}' ]; + /** * @property {number} textureCount - A local texture counter for multi-texture shaders. */ this.textureCount = 0; + + this.attributes = []; + + this.init(); }; /** -* @method PIXI.PixiShader#init +* Initialises the shader +* @method init +* */ PIXI.PixiShader.prototype.init = function() { - var program = PIXI.compileProgram(this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc); - var gl = PIXI.gl; + var gl = this.gl; + var program = PIXI.compileProgram(gl, this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc); + gl.useProgram(program); // get and store the uniforms for the shader @@ -52,8 +66,24 @@ PIXI.PixiShader.prototype.init = function() // get and store the attributes this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); 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. + // 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.aTextureCoord, this.colorAttribute]; + + // End worst hack eva // // add those custom shaders! for (var key in this.uniforms) @@ -72,12 +102,12 @@ PIXI.PixiShader.prototype.init = function() * 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 * -* @method PIXI.PixiShader#initUniforms +* @method initUniforms */ PIXI.PixiShader.prototype.initUniforms = function() { this.textureCount = 1; - + var gl = this.gl; var uniform; for (var key in this.uniforms) @@ -103,21 +133,21 @@ PIXI.PixiShader.prototype.initUniforms = function() if (type === 'mat2') { - uniform.glFunc = PIXI.gl.uniformMatrix2fv; + uniform.glFunc = gl.uniformMatrix2fv; } else if (type === 'mat3') { - uniform.glFunc = PIXI.gl.uniformMatrix3fv; + uniform.glFunc = gl.uniformMatrix3fv; } else if (type === 'mat4') { - uniform.glFunc = PIXI.gl.uniformMatrix4fv; + uniform.glFunc = gl.uniformMatrix4fv; } } else { // GL function reference - uniform.glFunc = PIXI.gl['uniform' + type]; + uniform.glFunc = gl['uniform' + type]; if (type === '2f' || type === '2i') { @@ -141,9 +171,9 @@ PIXI.PixiShader.prototype.initUniforms = function() }; /** -* Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture is has loaded) +* Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) * -* @method PIXI.PixiShader#initSampler2D +* @method initSampler2D */ PIXI.PixiShader.prototype.initSampler2D = function(uniform) { @@ -152,8 +182,10 @@ PIXI.PixiShader.prototype.initSampler2D = function(uniform) return; } - PIXI.gl.activeTexture(PIXI.gl['TEXTURE' + this.textureCount]); - PIXI.gl.bindTexture(PIXI.gl.TEXTURE_2D, uniform.value.baseTexture._glTexture); + var gl = this.gl; + + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTexture); // Extended texture data if (uniform.textureData) @@ -170,19 +202,19 @@ PIXI.PixiShader.prototype.initSampler2D = function(uniform) // 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 : PIXI.gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : PIXI.gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : PIXI.gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : PIXI.gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? PIXI.gl.LUMINANCE : PIXI.gl.RGBA; + 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 = PIXI.gl.REPEAT; - wrapT = PIXI.gl.REPEAT; + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; } - PIXI.gl.pixelStorei(PIXI.gl.UNPACK_FLIP_Y_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); if (data.width) { @@ -191,21 +223,21 @@ PIXI.PixiShader.prototype.initSampler2D = function(uniform) 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); - PIXI.gl.texImage2D(PIXI.gl.TEXTURE_2D, 0, format, width, height, border, format, PIXI.gl.UNSIGNED_BYTE, null); + 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); - PIXI.gl.texImage2D(PIXI.gl.TEXTURE_2D, 0, format, PIXI.gl.RGBA, PIXI.gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); } - PIXI.gl.texParameteri(PIXI.gl.TEXTURE_2D, PIXI.gl.TEXTURE_MAG_FILTER, magFilter); - PIXI.gl.texParameteri(PIXI.gl.TEXTURE_2D, PIXI.gl.TEXTURE_MIN_FILTER, minFilter); - PIXI.gl.texParameteri(PIXI.gl.TEXTURE_2D, PIXI.gl.TEXTURE_WRAP_S, wrapS); - PIXI.gl.texParameteri(PIXI.gl.TEXTURE_2D, PIXI.gl.TEXTURE_WRAP_T, wrapT); + 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); } - PIXI.gl.uniform1i(uniform.uniformLocation, this.textureCount); + gl.uniform1i(uniform.uniformLocation, this.textureCount); uniform._init = true; @@ -216,12 +248,13 @@ PIXI.PixiShader.prototype.initSampler2D = function(uniform) /** * Updates the shader uniform values. * -* @method PIXI.PixiShader#syncUniforms +* @method syncUniforms */ PIXI.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) @@ -233,32 +266,32 @@ PIXI.PixiShader.prototype.syncUniforms = function() { if (uniform.glMatrix === true) { - uniform.glFunc.call(PIXI.gl, uniform.uniformLocation, uniform.transpose, uniform.value); + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); } else { - uniform.glFunc.call(PIXI.gl, uniform.uniformLocation, uniform.value); + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); } } else if (uniform.glValueLength === 2) { - uniform.glFunc.call(PIXI.gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); } else if (uniform.glValueLength === 3) { - uniform.glFunc.call(PIXI.gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); } else if (uniform.glValueLength === 4) { - uniform.glFunc.call(PIXI.gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); + 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) { - PIXI.gl.activeTexture(PIXI.gl['TEXTURE' + this.textureCount]); - PIXI.gl.bindTexture(PIXI.gl.TEXTURE_2D, uniform.value.baseTexture._glTexture); - PIXI.gl.uniform1i(uniform.uniformLocation, this.textureCount); + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || PIXI.createWebGLTexture( uniform.value.baseTexture, gl)); + gl.uniform1i(uniform.uniformLocation, this.textureCount); this.textureCount++; } else @@ -270,22 +303,46 @@ PIXI.PixiShader.prototype.syncUniforms = function() }; +/** +* Destroys the shader +* @method destroy +* +*/ +PIXI.PixiShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attributes = null; +}; + +/** +* +* @property defaultVertexSrc +* @type String +*/ PIXI.PixiShader.defaultVertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', + 'attribute vec2 aColor;', 'uniform vec2 projectionVector;', 'uniform vec2 offsetVector;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', + '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 = aColor;', + ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = vec4(color * aColor.x, aColor.x);', '}' ]; + + + + diff --git a/src/pixi/renderers/webgl/PrimitiveShader.js b/src/pixi/renderers/webgl/shaders/PrimitiveShader.js similarity index 58% rename from src/pixi/renderers/webgl/PrimitiveShader.js rename to src/pixi/renderers/webgl/shaders/PrimitiveShader.js index b206e79a..c05109f3 100644 --- a/src/pixi/renderers/webgl/PrimitiveShader.js +++ b/src/pixi/renderers/webgl/shaders/PrimitiveShader.js @@ -2,12 +2,28 @@ * @author Mat Groves http://matgroves.com/ @Doormat23 */ - -PIXI.PrimitiveShader = function() +/** +* @class PrimitiveShader +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.PrimitiveShader = function(gl) { - // the webGL program.. + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + /** + * @property {any} program - The WebGL program. + */ this.program = null; + /** + * @property fragmentSrc + * @type Array + */ this.fragmentSrc = [ 'precision mediump float;', 'varying vec4 vColor;', @@ -17,6 +33,10 @@ PIXI.PrimitiveShader = function() '}' ]; + /** + * @property vertexSrc + * @type Array + */ this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec4 aColor;', @@ -24,35 +44,61 @@ PIXI.PrimitiveShader = function() 'uniform vec2 projectionVector;', 'uniform vec2 offsetVector;', 'uniform float alpha;', + '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 + 1.0 , 0.0, 1.0);', - ' vColor = aColor * alpha;', + ' vColor = aColor * vec4(tint * alpha, alpha);', '}' ]; + + this.init(); }; +/** +* Initialises the shader +* @method init +* +*/ PIXI.PrimitiveShader.prototype.init = function() { - var program = PIXI.compileProgram(this.vertexSrc, this.fragmentSrc); - var gl = PIXI.gl; + var gl = this.gl; + var program = PIXI.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'); + // 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 +* @method destroy +* +*/ +PIXI.PrimitiveShader.prototype.destroy = function() +{ + this.gl.deleteProgram( this.program ); + this.uniforms = null; + this.gl = null; + + this.attribute = null; +}; diff --git a/src/pixi/renderers/webgl/StripShader.js b/src/pixi/renderers/webgl/shaders/StripShader.js similarity index 85% rename from src/pixi/renderers/webgl/StripShader.js rename to src/pixi/renderers/webgl/shaders/StripShader.js index 55817b8b..ae654708 100644 --- a/src/pixi/renderers/webgl/StripShader.js +++ b/src/pixi/renderers/webgl/shaders/StripShader.js @@ -5,9 +5,14 @@ PIXI.StripShader = function() { - // the webGL program.. + /** + * @property {any} program - The WebGL program. + */ this.program = null; + /** + * @property {array} fragmentSrc - The fragment shader. + */ this.fragmentSrc = [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -21,14 +26,17 @@ PIXI.StripShader = function() '}' ]; + /** + * @property {array} fragmentSrc - The fragment shader. + */ this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute float aColor;', 'uniform mat3 translationMatrix;', 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', + 'uniform vec2 offsetVector;', 'varying float vColor;', 'void main(void) {', @@ -41,12 +49,17 @@ PIXI.StripShader = function() ]; }; +/** +* Initialises the shader +* @method init +* +*/ PIXI.StripShader.prototype.init = function() { - var program = PIXI.compileProgram(this.vertexSrc, this.fragmentSrc); var gl = PIXI.gl; + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); gl.useProgram(program); // get and store the uniforms for the shader diff --git a/src/pixi/renderers/webgl/utils/FilterTexture.js b/src/pixi/renderers/webgl/utils/FilterTexture.js new file mode 100644 index 00000000..00b3c8a3 --- /dev/null +++ b/src/pixi/renderers/webgl/utils/FilterTexture.js @@ -0,0 +1,84 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class FilterTexture +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +* @param width {Number} the horizontal range of the filter +* @param height {Number} the vertical range of the filter +* @private +*/ +PIXI.FilterTexture = function(gl, width, height) +{ + /** + * @property gl + * @type WebGLContext + */ + this.gl = gl; + + // next time to create a frame buffer and texture + this.frameBuffer = gl.createFramebuffer(); + this.texture = gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer ); + + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); + + this.resize(width, height); +}; + + +/** +* Clears the filter texture +* @method clear +*/ +PIXI.FilterTexture.prototype.clear = function() +{ + var gl = this.gl; + + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); +}; + +/** + * Resizes the texture to the specified width and height + * + * @method resize + * @param width {Number} the new width of the texture + * @param height {Number} the new height of the texture + */ +PIXI.FilterTexture.prototype.resize = function(width, height) +{ + if(this.width === width && this.height === height) return; + + this.width = width; + this.height = height; + + var gl = this.gl; + + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + +}; + +/** +* Destroys the filter texture +* @method destroy +*/ +PIXI.FilterTexture.prototype.destroy = function() +{ + var gl = this.gl; + gl.deleteFramebuffer( this.frameBuffer ); + gl.deleteTexture( this.texture ); + + this.frameBuffer = null; + this.texture = null; +}; diff --git a/src/pixi/renderers/webgl/utils/WebGLFastSpriteBatch.js b/src/pixi/renderers/webgl/utils/WebGLFastSpriteBatch.js new file mode 100644 index 00000000..f5a2e058 --- /dev/null +++ b/src/pixi/renderers/webgl/utils/WebGLFastSpriteBatch.js @@ -0,0 +1,351 @@ +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * + * Heavily inspired by LibGDX's WebGLSpriteBatch: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java + */ + +PIXI.WebGLFastSpriteBatch = function(gl) +{ + + + this.vertSize = 10; + this.maxSize = 6000;//Math.pow(2, 16) / this.vertSize; + this.size = this.maxSize; + + // console.log(this.size); + //the total number of floats in our batch + var numVerts = this.size * 4 * this.vertSize; + //the total number of indices in our batch + var numIndices = this.maxSize * 6; + + //vertex data + this.vertices = new Float32Array(numVerts); + //index data + this.indices = new Uint16Array(numIndices); + + this.vertexBuffer = null; + this.indexBuffer = null; + + this.lastIndexCount = 0; + + for (var i=0, j=0; i < numIndices; i += 6, j += 4) + { + this.indices[i + 0] = j + 0; + this.indices[i + 1] = j + 1; + this.indices[i + 2] = j + 2; + this.indices[i + 3] = j + 0; + this.indices[i + 4] = j + 2; + this.indices[i + 5] = j + 3; + } + + this.drawing = false; + this.currentBatchSize = 0; + this.currentBaseTexture = null; + + this.currentBlendMode = 0; + this.renderSession = null; + + + this.shader = null; + + this.matrix = null; + + this.setContext(gl); +}; + +PIXI.WebGLFastSpriteBatch.prototype.setContext = function(gl) +{ + this.gl = gl; + + // create a couple of buffers + this.vertexBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // 65535 is max index, so 65535 / 6 = 10922. + + + //upload the index data + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + this.currentBlendMode = 99999; +}; + +PIXI.WebGLFastSpriteBatch.prototype.begin = function(spriteBatch, renderSession) +{ + this.renderSession = renderSession; + this.shader = this.renderSession.shaderManager.fastShader; + + this.matrix = spriteBatch.worldTransform.toArray(true); + + // console.log(this.tempMatrix) + this.start(); +}; + +PIXI.WebGLFastSpriteBatch.prototype.end = function() +{ + this.flush(); +}; + + +PIXI.WebGLFastSpriteBatch.prototype.render = function(spriteBatch) +{ + + var children = spriteBatch.children; + var sprite = children[0]; + + // if the uvs have not updated then no point rendering just yet! + + // check texture. + if(!sprite.texture._uvs)return; + + this.currentBaseTexture = sprite.texture.baseTexture; + // check blend mode + if(sprite.blendMode !== this.currentBlendMode) + { + this.setBlendMode(sprite.blendMode); + } + + for(var i=0,j= children.length; i= this.size) + { + this.flush(); + } +}; + +PIXI.WebGLFastSpriteBatch.prototype.flush = function() +{ + + // If the batch is length 0 then return as there is nothing to draw + if (this.currentBatchSize===0)return; + + var gl = this.gl; + + // bind the current texture + + if(!this.currentBaseTexture._glTextures[gl.id])PIXI.createWebGLTexture(this.currentBaseTexture, gl); + + gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id]);// || PIXI.createWebGLTexture(this.currentBaseTexture, gl)); + + // upload the verts to the buffer + + + if(this.currentBatchSize > ( this.size * 0.5 ) ) + { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); + } + else + { + var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + } + + + // now draw those suckas! + gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0); + + // then reset the batch! + this.currentBatchSize = 0; + + // increment the draw count + this.renderSession.drawCount++; +}; + + +PIXI.WebGLFastSpriteBatch.prototype.stop = function() +{ + this.flush(); +}; + +PIXI.WebGLFastSpriteBatch.prototype.start = function() +{ + var gl = this.gl; + + // bind the main texture + gl.activeTexture(gl.TEXTURE0); + + // bind the buffers + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // set the projection + var projection = this.renderSession.projection; + gl.uniform2f(this.shader.projectionVector, projection.x, projection.y); + + // set the matrix + gl.uniformMatrix3fv(this.shader.uMatrix, false, this.matrix); + + // set the pointers + var stride = this.vertSize * 4; + + gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); + gl.vertexAttribPointer(this.shader.aPositionCoord, 2, gl.FLOAT, false, stride, 2 * 4); + gl.vertexAttribPointer(this.shader.aScale, 2, gl.FLOAT, false, stride, 4 * 4); + gl.vertexAttribPointer(this.shader.aRotation, 1, gl.FLOAT, false, stride, 6 * 4); + gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 7 * 4); + gl.vertexAttribPointer(this.shader.colorAttribute, 1, gl.FLOAT, false, stride, 9 * 4); + + // set the blend mode.. + if(this.currentBlendMode !== PIXI.blendModes.NORMAL) + { + this.setBlendMode(PIXI.blendModes.NORMAL); + } +}; + +PIXI.WebGLFastSpriteBatch.prototype.setBlendMode = function(blendMode) +{ + this.flush(); + + this.currentBlendMode = blendMode; + + var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; + this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); +}; + + diff --git a/src/pixi/renderers/webgl/WebGLFilterManager.js b/src/pixi/renderers/webgl/utils/WebGLFilterManager.js similarity index 62% rename from src/pixi/renderers/webgl/WebGLFilterManager.js rename to src/pixi/renderers/webgl/utils/WebGLFilterManager.js index 873b56c6..dd1303f4 100644 --- a/src/pixi/renderers/webgl/WebGLFilterManager.js +++ b/src/pixi/renderers/webgl/utils/WebGLFilterManager.js @@ -2,32 +2,69 @@ * @author Mat Groves http://matgroves.com/ @Doormat23 */ - -PIXI.WebGLFilterManager = function(transparent) +/** +* @class WebGLFilterManager +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +* @param transparent {Boolean} Whether or not the drawing context should be transparent +* @private +*/ +PIXI.WebGLFilterManager = function(gl, transparent) { this.transparent = transparent; this.filterStack = []; - this.texturePool = []; - + this.offsetX = 0; this.offsetY = 0; + this.setContext(gl); +}; + +// API +/** +* Initialises the context and the properties +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLFilterManager.prototype.setContext = function(gl) +{ + this.gl = gl; + this.texturePool = []; + this.initShaderBuffers(); }; -// API - -PIXI.WebGLFilterManager.prototype.begin = function(projection, buffer) +/** +* +* @method begin +* @param renderSession {RenderSession} +* @param buffer {ArrayBuffer} +*/ +PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer) { + this.renderSession = renderSession; + this.defaultShader = renderSession.shaderManager.defaultShader; + + var projection = this.renderSession.projection; + this.width = projection.x * 2; this.height = -projection.y * 2; this.buffer = buffer; }; +/** +* Applies the filter and adds it to the current filter stack +* @method pushFilter +* @param filterBlock {Object} TODO-Alvin +*/ PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) { - var gl = PIXI.gl; + var gl = this.gl; + + var projection = this.renderSession.projection; + var offset = this.renderSession.offset; + // filter program // OPTIMISATION - the first filter is free if its a simple color change? @@ -41,7 +78,7 @@ PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) var texture = this.texturePool.pop(); if(!texture) { - texture = new PIXI.FilterTexture(this.width, this.height); + texture = new PIXI.FilterTexture(this.gl, this.width, this.height); } else { @@ -50,7 +87,13 @@ PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) gl.bindTexture(gl.TEXTURE_2D, texture.texture); - this.getBounds(filterBlock.target); +// this.getBounds(filterBlock.target); + + filterBlock.target.filterArea = filterBlock.target.getBounds(); + // console.log(filterBlock.target.filterArea) + // console.log(filterBlock.target.filterArea); + // addpadding? + //displayObject.filterArea.x var filterArea = filterBlock.target.filterArea; @@ -69,34 +112,45 @@ PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); + //console.log(filterArea) // set view port gl.viewport(0, 0, filterArea.width, filterArea.height); - PIXI.projection.x = filterArea.width/2; - PIXI.projection.y = -filterArea.height/2; + projection.x = filterArea.width/2; + projection.y = -filterArea.height/2; - PIXI.offset.x = -filterArea.x; - PIXI.offset.y = -filterArea.y; + offset.x = -filterArea.x; + offset.y = -filterArea.y; + //console.log(PIXI.defaultShader.projectionVector) // update projection - gl.uniform2f(PIXI.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - gl.uniform2f(PIXI.defaultShader.offsetVector, -filterArea.x, -filterArea.y); + gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); + gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); //PIXI.primitiveProgram gl.colorMask(true, true, true, true); gl.clearColor(0,0,0, 0); gl.clear(gl.COLOR_BUFFER_BIT); + //filter.texture = texture; filterBlock._glFilterTexture = texture; + + //console.log("PUSH") }; +/** +* Removes the last filter from the filter stack and doesn't return it +* @method popFilter +*/ PIXI.WebGLFilterManager.prototype.popFilter = function() { - var gl = PIXI.gl; + var gl = this.gl; var filterBlock = this.filterStack.pop(); var filterArea = filterBlock.target.filterArea; var texture = filterBlock._glFilterTexture; + var projection = this.renderSession.projection; + var offset = this.renderSession.offset; if(filterBlock.filterPasses.length > 1) { @@ -119,7 +173,7 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // nnow set the uvs.. + // now set the uvs.. this.uvArray[2] = filterArea.width/this.width; this.uvArray[5] = filterArea.height/this.height; this.uvArray[6] = filterArea.width/this.width; @@ -129,9 +183,9 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() var inputTexture = texture; var outputTexture = this.texturePool.pop(); - if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.width, this.height); + if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width, this.height); - // need to clear this FBO as it may have some left over elements from a prvious filter. + // need to clear this FBO as it may have some left over elements from a previous filter. gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); gl.clear(gl.COLOR_BUFFER_BIT); @@ -199,11 +253,11 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() // TODO need toremove thease global elements.. - PIXI.projection.x = sizeX/2; - PIXI.projection.y = -sizeY/2; + projection.x = sizeX/2; + projection.y = -sizeY/2; - PIXI.offset.x = offsetX; - PIXI.offset.y = offsetY; + offset.x = offsetX; + offset.y = offsetY; filterArea = filterBlock.target.filterArea; @@ -241,6 +295,9 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() // bind the buffer gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); + // set the blend mode! + //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) + // set texture gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture.texture); @@ -250,30 +307,39 @@ PIXI.WebGLFilterManager.prototype.popFilter = function() this.applyFilterPass(filter, filterArea, sizeX, sizeY); // now restore the regular shader.. - gl.useProgram(PIXI.defaultShader.program); - gl.uniform2f(PIXI.defaultShader.projectionVector, sizeX/2, -sizeY/2); - gl.uniform2f(PIXI.defaultShader.offsetVector, -offsetX, -offsetY); + gl.useProgram(this.defaultShader.program); + gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); + gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); // return the texture to the pool this.texturePool.push(texture); filterBlock._glFilterTexture = null; }; + +/** +* Applies the filter to the specified area +* @method applyFilterPass +* @param filter {AbstractFilter} the filter that needs to be applied +* @param filterArea {texture} TODO - might need an update +* @param width {Number} the horizontal range of the filter +* @param height {Number} the vertical range of the filter +*/ PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height) { // use program - var gl = PIXI.gl; - var shader = filter.shader; + var gl = this.gl; + var shader = filter.shaders[gl.id]; if(!shader) { - shader = new PIXI.PixiShader(); + shader = new PIXI.PixiShader(gl); shader.fragmentSrc = filter.fragmentSrc; shader.uniforms = filter.uniforms; shader.init(); - filter.shader = shader; + filter.shaders[gl.id] = shader; } // set the shader @@ -284,10 +350,12 @@ PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, if(filter.uniforms.dimensions) { + //console.log(filter.uniforms.dimensions) filter.uniforms.dimensions.value[0] = this.width;//width; filter.uniforms.dimensions.value[1] = this.height;//height; filter.uniforms.dimensions.value[2] = this.vertexArray[0]; filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; + // console.log(this.vertexArray[5]) } shader.syncUniforms(); @@ -298,23 +366,34 @@ PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); // draw the filter... gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + this.renderSession.drawCount++; }; +/** +* Initialises the shader buffers +* @method initShaderBuffers +*/ PIXI.WebGLFilterManager.prototype.initShaderBuffers = function() { - var gl = PIXI.gl; + var gl = this.gl; // create some buffers this.vertexBuffer = gl.createBuffer(); this.uvBuffer = gl.createBuffer(); + this.colorBuffer = gl.createBuffer(); this.indexBuffer = gl.createBuffer(); + // bind and upload the vertexs.. - // keep a refferance to the vertexFloatData.. + // keep a reference to the vertexFloatData.. this.vertexArray = new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, @@ -339,6 +418,17 @@ PIXI.WebGLFilterManager.prototype.initShaderBuffers = function() this.uvArray, gl.STATIC_DRAW); + this.colorArray = new Float32Array([1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + this.colorArray, + gl.STATIC_DRAW); + // bind and upload the index gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData( @@ -347,150 +437,29 @@ PIXI.WebGLFilterManager.prototype.initShaderBuffers = function() gl.STATIC_DRAW); }; -PIXI.WebGLFilterManager.prototype.getBounds = function(displayObject) +/** +* TODO-Alvin +* @method destroy +*/ +PIXI.WebGLFilterManager.prototype.destroy = function() { - // time to get the width and height of the object! - var worldTransform, width, height, aX, aY, w0, w1, h0, h1, doTest; - var a, b, c, d, tx, ty, x1, x2, x3, x4, y1, y2, y3, y4; + var gl = this.gl; - var tempObject = displayObject.first; - var testObject = displayObject.last._iNext; - - var maxX = -Infinity; - var maxY = -Infinity; - - var minX = Infinity; - var minY = Infinity; - - do - { - // TODO can be optimized! - what if there is no scale / rotation? - - if(tempObject.visible) - { - if(tempObject instanceof PIXI.Sprite) - { - width = tempObject.texture.frame.width; - height = tempObject.texture.frame.height; - - // TODO trim?? - aX = tempObject.anchor.x; - aY = tempObject.anchor.y; - w0 = width * (1-aX); - w1 = width * -aX; - - h0 = height * (1-aY); - h1 = height * -aY; - - doTest = true; - } - else if(tempObject instanceof PIXI.Graphics) - { - tempObject.updateFilterBounds(); - - var bounds = tempObject.bounds; - - width = bounds.width; - height = bounds.height; - - w0 = bounds.x; - w1 = bounds.x + bounds.width; - - h0 = bounds.y; - h1 = bounds.y + bounds.height; - - doTest = true; - } - } - - if(doTest) - { - worldTransform = tempObject.worldTransform; - - a = worldTransform[0]; - b = worldTransform[3]; - c = worldTransform[1]; - d = worldTransform[4]; - tx = worldTransform[2]; - ty = worldTransform[5]; - - x1 = a * w1 + c * h1 + tx; - y1 = d * h1 + b * w1 + ty; - - x2 = a * w0 + c * h1 + tx; - y2 = d * h1 + b * w0 + ty; - - x3 = a * w0 + c * h0 + tx; - y3 = d * h0 + b * w0 + ty; - - x4 = a * w1 + c * h0 + tx; - y4 = d * h0 + b * w1 + ty; - - minX = x1 < minX ? x1 : minX; - minX = x2 < minX ? x2 : minX; - minX = x3 < minX ? x3 : minX; - minX = x4 < minX ? x4 : minX; - - minY = y1 < minY ? y1 : minY; - minY = y2 < minY ? y2 : minY; - minY = y3 < minY ? y3 : minY; - minY = y4 < minY ? y4 : minY; - - maxX = x1 > maxX ? x1 : maxX; - maxX = x2 > maxX ? x2 : maxX; - maxX = x3 > maxX ? x3 : maxX; - maxX = x4 > maxX ? x4 : maxX; - - maxY = y1 > maxY ? y1 : maxY; - maxY = y2 > maxY ? y2 : maxY; - maxY = y3 > maxY ? y3 : maxY; - maxY = y4 > maxY ? y4 : maxY; - } - - doTest = false; - tempObject = tempObject._iNext; + this.filterStack = null; + + this.offsetX = 0; + this.offsetY = 0; + // destroy textures + for (var i = 0; i < this.texturePool.length; i++) { + this.texturePool.destroy(); } - while(tempObject !== testObject); + + this.texturePool = null; - displayObject.filterArea.x = minX; - displayObject.filterArea.y = minY; - - displayObject.filterArea.width = maxX - minX; - displayObject.filterArea.height = maxY - minY; -}; - -PIXI.FilterTexture = function(width, height) -{ - var gl = PIXI.gl; - - // next time to create a frame buffer and texture - this.frameBuffer = gl.createFramebuffer(); - this.texture = gl.createTexture(); - - gl.bindTexture(gl.TEXTURE_2D, this.texture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer ); - - gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); - - this.resize(width, height); -}; - -PIXI.FilterTexture.prototype.resize = function(width, height) -{ - if(this.width === width && this.height === height) return; - - this.width = width; - this.height = height; - - var gl = PIXI.gl; - - gl.bindTexture(gl.TEXTURE_2D, this.texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - -}; + //destroy buffers.. + gl.deleteBuffer(this.vertexBuffer); + gl.deleteBuffer(this.uvBuffer); + gl.deleteBuffer(this.colorBuffer); + gl.deleteBuffer(this.indexBuffer); +}; \ No newline at end of file diff --git a/src/pixi/renderers/webgl/WebGLGraphics.js b/src/pixi/renderers/webgl/utils/WebGLGraphics.js similarity index 77% rename from src/pixi/renderers/webgl/WebGLGraphics.js rename to src/pixi/renderers/webgl/utils/WebGLGraphics.js index bb90529d..06f1be12 100644 --- a/src/pixi/renderers/webgl/WebGLGraphics.js +++ b/src/pixi/renderers/webgl/utils/WebGLGraphics.js @@ -5,7 +5,9 @@ /** * A set of functions used by the webGL renderer to draw the primitive graphics data * - * @class CanvasGraphics + * @class WebGLGraphics + * @private + * @static */ PIXI.WebGLGraphics = function() { @@ -19,16 +21,21 @@ PIXI.WebGLGraphics = function() * @private * @method renderGraphics * @param graphics {Graphics} - * @param projection {Object} + * @param renderSession {Object} */ -PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) +PIXI.WebGLGraphics.renderGraphics = function(graphics, renderSession)//projection, offset) { - var gl = PIXI.gl; + var gl = renderSession.gl; + var projection = renderSession.projection, + offset = renderSession.offset, + shader = renderSession.shaderManager.primitiveShader; - if(!graphics._webGL)graphics._webGL = {points:[], indices:[], lastIndex:0, + if(!graphics._webGL[gl.id])graphics._webGL[gl.id] = {points:[], indices:[], lastIndex:0, buffer:gl.createBuffer(), indexBuffer:gl.createBuffer()}; + var webGL = graphics._webGL[gl.id]; + if(graphics.dirty) { graphics.dirty = false; @@ -37,43 +44,41 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) { graphics.clearDirty = false; - graphics._webGL.lastIndex = 0; - graphics._webGL.points = []; - graphics._webGL.indices = []; + webGL.lastIndex = 0; + webGL.points = []; + webGL.indices = []; } - PIXI.WebGLGraphics.updateGraphics(graphics); + PIXI.WebGLGraphics.updateGraphics(graphics, gl); } - PIXI.activatePrimitiveShader(); + renderSession.shaderManager.activatePrimitiveShader(); - // This could be speeded up fo sure! - var m = PIXI.mat3.clone(graphics.worldTransform); + // This could be speeded up for sure! - PIXI.mat3.transpose(m); - - // set the matrix transform for the + // set the matrix transform gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - gl.uniformMatrix3fv(PIXI.primitiveShader.translationMatrix, false, m); + gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); - gl.uniform2f(PIXI.primitiveShader.projectionVector, projection.x, -projection.y); - gl.uniform2f(PIXI.primitiveShader.offsetVector, -PIXI.offset.x, -PIXI.offset.y); + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); - gl.uniform1f(PIXI.primitiveShader.alpha, graphics.worldAlpha); - gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); + gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); - gl.vertexAttribPointer(PIXI.primitiveShader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); - gl.vertexAttribPointer(PIXI.primitiveShader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); + gl.uniform1f(shader.alpha, graphics.worldAlpha); + gl.bindBuffer(gl.ARRAY_BUFFER, webGL.buffer); + + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); + gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); // set the index buffer! - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.indexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGL.indexBuffer); + gl.drawElements(gl.TRIANGLE_STRIP, webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); - gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); - - PIXI.deactivatePrimitiveShader(); + renderSession.shaderManager.deactivatePrimitiveShader(); // return to default shader... // PIXI.activateShader(PIXI.defaultShader); @@ -85,11 +90,14 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) * @static * @private * @method updateGraphics - * @param graphics {Graphics} + * @param graphicsData {Graphics} The graphics object to update + * @param gl {WebGLContext} the current WebGL drawing context */ -PIXI.WebGLGraphics.updateGraphics = function(graphics) +PIXI.WebGLGraphics.updateGraphics = function(graphics, gl) { - for (var i = graphics._webGL.lastIndex; i < graphics.graphicsData.length; i++) + var webGL = graphics._webGL[gl.id]; + + for (var i = webGL.lastIndex; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; @@ -98,37 +106,37 @@ PIXI.WebGLGraphics.updateGraphics = function(graphics) if(data.fill) { if(data.points.length>3) - PIXI.WebGLGraphics.buildPoly(data, graphics._webGL); + PIXI.WebGLGraphics.buildPoly(data, webGL); } if(data.lineWidth > 0) { - PIXI.WebGLGraphics.buildLine(data, graphics._webGL); + PIXI.WebGLGraphics.buildLine(data, webGL); } } else if(data.type === PIXI.Graphics.RECT) { - PIXI.WebGLGraphics.buildRectangle(data, graphics._webGL); + PIXI.WebGLGraphics.buildRectangle(data, webGL); } else if(data.type === PIXI.Graphics.CIRC || data.type === PIXI.Graphics.ELIP) { - PIXI.WebGLGraphics.buildCircle(data, graphics._webGL); + PIXI.WebGLGraphics.buildCircle(data, webGL); } } - graphics._webGL.lastIndex = graphics.graphicsData.length; + webGL.lastIndex = graphics.graphicsData.length; - var gl = PIXI.gl; + - graphics._webGL.glPoints = new Float32Array(graphics._webGL.points); + webGL.glPoints = new Float32Array(webGL.points); - gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); - gl.bufferData(gl.ARRAY_BUFFER, graphics._webGL.glPoints, gl.STATIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, webGL.buffer); + gl.bufferData(gl.ARRAY_BUFFER, webGL.glPoints, gl.STATIC_DRAW); - graphics._webGL.glIndicies = new Uint16Array(graphics._webGL.indices); + webGL.glIndicies = new Uint16Array(webGL.indices); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.glIndicies, gl.STATIC_DRAW); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGL.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, webGL.glIndicies, gl.STATIC_DRAW); }; /** @@ -137,7 +145,7 @@ PIXI.WebGLGraphics.updateGraphics = function(graphics) * @static * @private * @method buildRectangle - * @param graphics {Graphics} + * @param graphicsData {Graphics} The graphics object to draw TODO-Alvin * @param webGLData {Object} */ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) @@ -185,13 +193,18 @@ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) if(graphicsData.lineWidth) { + var tempPoints = graphicsData.points; + graphicsData.points = [x, y, x + width, y, x + width, y + height, x, y + height, x, y]; + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; } }; @@ -201,7 +214,7 @@ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) * @static * @private * @method buildCircle - * @param graphics {Graphics} + * @param graphicsData {Graphics} The graphics object to draw * @param webGLData {Object} */ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) @@ -252,6 +265,8 @@ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) if(graphicsData.lineWidth) { + var tempPoints = graphicsData.points; + graphicsData.points = []; for (i = 0; i < totalSegs + 1; i++) @@ -261,6 +276,8 @@ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) } PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; } }; @@ -270,7 +287,7 @@ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) * @static * @private * @method buildLine - * @param graphics {Graphics} + * @param graphicsData {Graphics} The graphics object to draw TODO-Alvin * @param webGLData {Object} */ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) @@ -293,7 +310,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) var firstPoint = new PIXI.Point( points[0], points[1] ); var lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); - // if the first point is the last point - goona have issues :) + // if the first point is the last point - gonna have issues :) if(firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) { points.pop(); @@ -480,7 +497,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) * @static * @private * @method buildPoly - * @param graphics {Graphics} + * @param graphicsData {Graphics} The graphics object to draw TODO-Alvin * @param webGLData {Object} */ PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) diff --git a/src/pixi/renderers/webgl/utils/WebGLMaskManager.js b/src/pixi/renderers/webgl/utils/WebGLMaskManager.js new file mode 100644 index 00000000..12a78494 --- /dev/null +++ b/src/pixi/renderers/webgl/utils/WebGLMaskManager.js @@ -0,0 +1,97 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** +* @class WebGLMaskManager +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +* @private +*/ +PIXI.WebGLMaskManager = function(gl) +{ + this.maskStack = []; + this.maskPosition = 0; + + this.setContext(gl); +}; + +/** +* Sets the drawing context to the one given in parameter +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLMaskManager.prototype.setContext = function(gl) +{ + this.gl = gl; +}; + +/** +* Applies the Mask and adds it to the current filter stack +* @method pushMask +* @param maskData {Array} +* @param renderSession {RenderSession} +*/ +PIXI.WebGLMaskManager.prototype.pushMask = function(maskData, renderSession) +{ + var gl = this.gl; + + if(this.maskStack.length === 0) + { + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc(gl.ALWAYS,1,1); + } + + // maskData.visible = false; + + this.maskStack.push(maskData); + + gl.colorMask(false, false, false, true); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + + PIXI.WebGLGraphics.renderGraphics(maskData, renderSession); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL,0, this.maskStack.length); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); +}; + +/** +* Removes the last filter from the filter stack and doesn't return it +* @method popMask +* +* @param renderSession {RenderSession} TODO-Alvin +*/ +PIXI.WebGLMaskManager.prototype.popMask = function(renderSession) +{ + var gl = this.gl; + + var maskData = this.maskStack.pop(); + + if(maskData) + { + gl.colorMask(false, false, false, false); + + //gl.stencilFunc(gl.ALWAYS,1,1); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + + PIXI.WebGLGraphics.renderGraphics(maskData, renderSession); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL,0,this.maskStack.length); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); + } + + if(this.maskStack.length === 0)gl.disable(gl.STENCIL_TEST); +}; + +/** +* TODO-Alvin +* @method destroy +*/ +PIXI.WebGLMaskManager.prototype.destroy = function() +{ + this.maskStack = null; + this.gl = null; +}; \ No newline at end of file diff --git a/src/pixi/renderers/webgl/utils/WebGLShaderManager.js b/src/pixi/renderers/webgl/utils/WebGLShaderManager.js new file mode 100644 index 00000000..17e5937f --- /dev/null +++ b/src/pixi/renderers/webgl/utils/WebGLShaderManager.js @@ -0,0 +1,161 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** +* @class WebGLShaderManager +* @constructor +* @param gl {WebGLContext} the current WebGL drawing context +* @private +*/ +PIXI.WebGLShaderManager = function(gl) +{ + + this.maxAttibs = 10; + this.attribState = []; + this.tempAttribState = []; + + for (var i = 0; i < this.maxAttibs; i++) { + this.attribState[i] = false; + } + + this.setContext(gl); + // the final one is used for the rendering strips + //this.stripShader = new PIXI.StripShader(gl); +}; + + +/** +* Initialises the context and the properties +* @method setContext +* @param gl {WebGLContext} the current WebGL drawing context +* @param transparent {Boolean} Whether or not the drawing context should be transparent +*/ +PIXI.WebGLShaderManager.prototype.setContext = function(gl) +{ + this.gl = gl; + + // the next one is used for rendering primatives + this.primitiveShader = new PIXI.PrimitiveShader(gl); + + // this shader is used for the default sprite rendering + this.defaultShader = new PIXI.PixiShader(gl); + + // this shader is used for the fast sprite rendering + this.fastShader = new PIXI.PixiFastShader(gl); + + + this.activateShader(this.defaultShader); +}; + + +/** +* Initialises the context and the properties +* @method setAttribs +* @param attribs {Array} TODO-Alvin +*/ +PIXI.WebGLShaderManager.prototype.setAttribs = function(attribs) +{ + // reset temp state + + var i; + + for (i = 0; i < this.tempAttribState.length; i++) + { + this.tempAttribState[i] = false; + } + + // set the new attribs + for (i = 0; i < attribs.length; i++) + { + var attribId = attribs[i]; + this.tempAttribState[attribId] = true; + } + + var gl = this.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]) + { + gl.enableVertexAttribArray(i); + } + else + { + gl.disableVertexAttribArray(i); + } + } + } + + // console.log(this.tempAttribState) +}; + +/** +* TODO-Alvin +* @method activateShader +* @param shader {Object} the shader that is going to be activated +*/ +PIXI.WebGLShaderManager.prototype.activateShader = function(shader) +{ + //if(this.currentShader == shader)return; + + this.currentShader = shader; + // console.log(shader.program) + this.gl.useProgram(shader.program); + this.setAttribs(shader.attributes); + + // console.log(shader.attributes) + +}; + +/** +* Triggers the primitive shader +* @method activatePrimitiveShader +*/ +PIXI.WebGLShaderManager.prototype.activatePrimitiveShader = function() +{ + var gl = this.gl; + + gl.useProgram(this.primitiveShader.program); + + this.setAttribs(this.primitiveShader.attributes); + +}; + +/** +* Disable the primitive shader +* @method deactivatePrimitiveShader +*/ +PIXI.WebGLShaderManager.prototype.deactivatePrimitiveShader = function() +{ + var gl = this.gl; + + gl.useProgram(this.defaultShader.program); + + this.setAttribs(this.defaultShader.attributes); +}; + +/** +* Destroys +* @method destroy +*/ +PIXI.WebGLShaderManager.prototype.destroy = function() +{ + this.attribState = null; + + this.tempAttribState = null; + + this.primitiveShader.destroy(); + + this.defaultShader.destroy(); + + this.fastShader.destroy(); + + this.gl = null; +}; + diff --git a/src/pixi/renderers/webgl/utils/WebGLShaderUtils.js b/src/pixi/renderers/webgl/utils/WebGLShaderUtils.js new file mode 100644 index 00000000..a9a7f338 --- /dev/null +++ b/src/pixi/renderers/webgl/utils/WebGLShaderUtils.js @@ -0,0 +1,55 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.initDefaultShaders = function() +{ + + // PIXI.stripShader = new PIXI.StripShader(); +// PIXI.stripShader.init(); + +}; + +PIXI.CompileVertexShader = function(gl, shaderSrc) +{ + return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); +}; + +PIXI.CompileFragmentShader = function(gl, shaderSrc) +{ + return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); +}; + +PIXI._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; +}; + +PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc) +{ + //var gl = PIXI.gl; + var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc); + var vertexShader = PIXI.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/pixi/renderers/webgl/utils/WebGLSpriteBatch.js b/src/pixi/renderers/webgl/utils/WebGLSpriteBatch.js new file mode 100644 index 00000000..8958a98e --- /dev/null +++ b/src/pixi/renderers/webgl/utils/WebGLSpriteBatch.js @@ -0,0 +1,496 @@ +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * + * Heavily inspired by LibGDX's WebGLSpriteBatch: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java + */ + + /** + * + * @class WebGLSpriteBatch + * @private + * @constructor + * @param gl {WebGLContext} the current WebGL drawing context + * + */ +PIXI.WebGLSpriteBatch = function(gl) +{ + + /** + * TODO-Alvin + * + * @property vertSize + * @type Number + */ + this.vertSize = 6; + + /** + * TODO-Alvin + * @property size + * @type Number + */ + this.size = 10000;//Math.pow(2, 16) / this.vertSize; + + // console.log(this.size); + //the total number of floats in our batch + var numVerts = this.size * 4 * this.vertSize; + //the total number of indices in our batch + var numIndices = this.size * 6; + + //vertex data + + /** + * Holds the vertices + * + * @property vertices + * @type Float32Array + */ + this.vertices = new Float32Array(numVerts); + + //index data + /** + * Holds the indices + * + * @property indices + * @type Uint16Array + */ + this.indices = new Uint16Array(numIndices); + + this.lastIndexCount = 0; + + for (var i=0, j=0; i < numIndices; i += 6, j += 4) + { + this.indices[i + 0] = j + 0; + this.indices[i + 1] = j + 1; + this.indices[i + 2] = j + 2; + this.indices[i + 3] = j + 0; + this.indices[i + 4] = j + 2; + this.indices[i + 5] = j + 3; + } + + + this.drawing = false; + this.currentBatchSize = 0; + this.currentBaseTexture = null; + + this.setContext(gl); +}; + +/** +* +* @method setContext +* +* @param gl {WebGLContext} the current WebGL drawing context +*/ +PIXI.WebGLSpriteBatch.prototype.setContext = function(gl) +{ + this.gl = gl; + + // create a couple of buffers + this.vertexBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // 65535 is max index, so 65535 / 6 = 10922. + + + //upload the index data + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + this.currentBlendMode = 99999; +}; + +/** +* +* @method begin +* +* @param renderSession {RenderSession} the RenderSession +*/ +PIXI.WebGLSpriteBatch.prototype.begin = function(renderSession) +{ + this.renderSession = renderSession; + this.shader = this.renderSession.shaderManager.defaultShader; + + this.start(); +}; + +/** +* +* @method end +* +*/ +PIXI.WebGLSpriteBatch.prototype.end = function() +{ + this.flush(); +}; + +/** +* +* @method render +* +* @param sprite {Sprite} the sprite to render TODO-Alvin +*/ +PIXI.WebGLSpriteBatch.prototype.render = function(sprite) +{ + // check texture.. + if(sprite.texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size) + { + this.flush(); + this.currentBaseTexture = sprite.texture.baseTexture; + } + + + // check blend mode + if(sprite.blendMode !== this.currentBlendMode) + { + this.setBlendMode(sprite.blendMode); + } + + // get the uvs for the texture + var uvs = sprite._uvs || sprite.texture._uvs; + // if the uvs have not updated then no point rendering just yet! + if(!uvs)return; + + // get the sprites current alpha + var alpha = sprite.worldAlpha; + var tint = sprite.tint; + + var verticies = this.vertices; + + var width = sprite.texture.frame.width; + var height = sprite.texture.frame.height; + + // TODO trim?? + var aX = sprite.anchor.x; + var aY = sprite.anchor.y; + + var w0, w1, h0, h1; + + if (sprite.texture.trimmed) + { + // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. + var trim = sprite.texture.trim; + + w1 = trim.x - aX * trim.realWidth; + w0 = w1 + width; + + h1 = trim.y - aY * trim.realHeight; + h0 = h1 + height; + } + else + { + w0 = (width ) * (1-aX); + w1 = (width ) * -aX; + + h0 = height * (1-aY); + h1 = height * -aY; + } + + var index = this.currentBatchSize * 4 * this.vertSize; + + var worldTransform = sprite.worldTransform;//.toArray(); + + var a = worldTransform.a;//[0]; + var b = worldTransform.c;//[3]; + var c = worldTransform.b;//[1]; + var d = worldTransform.d;//[4]; + var tx = worldTransform.tx;//[2]; + var ty = worldTransform.ty;///[5]; + + // xy + verticies[index++] = a * w1 + c * h1 + tx; + verticies[index++] = d * h1 + b * w1 + ty; + // uv + verticies[index++] = uvs.x0; + verticies[index++] = uvs.y0; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h1 + tx; + verticies[index++] = d * h1 + b * w0 + ty; + // uv + verticies[index++] = uvs.x1; + verticies[index++] = uvs.y1; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h0 + tx; + verticies[index++] = d * h0 + b * w0 + ty; + // uv + verticies[index++] = uvs.x2; + verticies[index++] = uvs.y2; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w1 + c * h0 + tx; + verticies[index++] = d * h0 + b * w1 + ty; + // uv + verticies[index++] = uvs.x3; + verticies[index++] = uvs.y3; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // increment the batchsize + this.currentBatchSize++; + + +}; + +/** +* +* @method renderTilingSprite +* +* @param sprite {TilingSprite} the sprite to render TODO-Alvin +*/ +PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite) +{ + var texture = tilingSprite.tilingTexture; + + if(texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size) + { + this.flush(); + this.currentBaseTexture = texture.baseTexture; + } + + // check blend mode + if(tilingSprite.blendMode !== this.currentBlendMode) + { + this.setBlendMode(tilingSprite.blendMode); + } + + // set the textures uvs temporarily + // TODO create a separate texture so that we can tile part of a texture + + if(!tilingSprite._uvs)tilingSprite._uvs = new Float32Array(8); + + var uvs = tilingSprite._uvs; + + tilingSprite.tilePosition.x %= texture.baseTexture.width; + tilingSprite.tilePosition.y %= texture.baseTexture.height; + + var offsetX = tilingSprite.tilePosition.x/texture.baseTexture.width; + var offsetY = tilingSprite.tilePosition.y/texture.baseTexture.height; + + var scaleX = (tilingSprite.width / texture.baseTexture.width) / (tilingSprite.tileScale.x * tilingSprite.tileScaleOffset.x); + var scaleY = (tilingSprite.height / texture.baseTexture.height) / (tilingSprite.tileScale.y * tilingSprite.tileScaleOffset.y); + + uvs.x0 = 0 - offsetX; + uvs.y0 = 0 - offsetY; + + uvs.x1 = (1 * scaleX) - offsetX; + uvs.y1 = 0 - offsetY; + + uvs.x2 = (1 * scaleX) - offsetX; + uvs.y2 = (1 * scaleY) - offsetY; + + uvs.x3 = 0 - offsetX; + uvs.y3 = (1 *scaleY) - offsetY; + + + // get the tilingSprites current alpha + var alpha = tilingSprite.worldAlpha; + var tint = tilingSprite.tint; + + var verticies = this.vertices; + + var width = tilingSprite.width; + var height = tilingSprite.height; + + // TODO trim?? + var aX = tilingSprite.anchor.x; // - tilingSprite.texture.trim.x + var aY = tilingSprite.anchor.y; //- tilingSprite.texture.trim.y + var w0 = width * (1-aX); + var w1 = width * -aX; + + var h0 = height * (1-aY); + var h1 = height * -aY; + + var index = this.currentBatchSize * 4 * this.vertSize; + + var worldTransform = tilingSprite.worldTransform; + + var a = worldTransform.a;//[0]; + var b = worldTransform.c;//[3]; + var c = worldTransform.b;//[1]; + var d = worldTransform.d;//[4]; + var tx = worldTransform.tx;//[2]; + var ty = worldTransform.ty;///[5]; + + // xy + verticies[index++] = a * w1 + c * h1 + tx; + verticies[index++] = d * h1 + b * w1 + ty; + // uv + verticies[index++] = uvs.x0; + verticies[index++] = uvs.y0; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h1 + tx; + verticies[index++] = d * h1 + b * w0 + ty; + // uv + verticies[index++] = uvs.x1; + verticies[index++] = uvs.y1; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h0 + tx; + verticies[index++] = d * h0 + b * w0 + ty; + // uv + verticies[index++] = uvs.x2; + verticies[index++] = uvs.y2; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w1 + c * h0 + tx; + verticies[index++] = d * h0 + b * w1 + ty; + // uv + verticies[index++] = uvs.x3; + verticies[index++] = uvs.y3; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // increment the batchs + this.currentBatchSize++; +}; + + +/** +* +* +* @method flush TODO-Alvin +* +*/ +PIXI.WebGLSpriteBatch.prototype.flush = function() +{ + // If the batch is length 0 then return as there is nothing to draw + if (this.currentBatchSize===0)return; + + var gl = this.gl; + + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id] || PIXI.createWebGLTexture(this.currentBaseTexture, gl)); + + // upload the verts to the buffer + + if(this.currentBatchSize > ( this.size * 0.5 ) ) + { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); + } + else + { + var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + } + + // var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); + //gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + + // now draw those suckas! + gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0); + + // then reset the batch! + this.currentBatchSize = 0; + + // increment the draw count + this.renderSession.drawCount++; +}; + +/** +* +* @method stop +* +*/ +PIXI.WebGLSpriteBatch.prototype.stop = function() +{ + this.flush(); +}; + +/** +* +* @method start +* +*/ +PIXI.WebGLSpriteBatch.prototype.start = function() +{ + var gl = this.gl; + + // bind the main texture + gl.activeTexture(gl.TEXTURE0); + + // bind the buffers + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // set the projection + var projection = this.renderSession.projection; + gl.uniform2f(this.shader.projectionVector, projection.x, projection.y); + + // set the pointers + var stride = this.vertSize * 4; + gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); + gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4); + gl.vertexAttribPointer(this.shader.colorAttribute, 2, gl.FLOAT, false, stride, 4 * 4); + + // set the blend mode.. + if(this.currentBlendMode !== PIXI.blendModes.NORMAL) + { + this.setBlendMode(PIXI.blendModes.NORMAL); + } +}; + +/** +* +* @method setBlendMode +* +* @param blendMode {Number} the blendMode, should be a Pixi const, such as PIXI.BlendModes.ADD +* TODO-Alvin +*/ +PIXI.WebGLSpriteBatch.prototype.setBlendMode = function(blendMode) +{ + this.flush(); + + this.currentBlendMode = blendMode; + + var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; + this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); +}; + +/** +* Destroys the SpriteBatch +* @method destroy +*/ +PIXI.WebGLSpriteBatch.prototype.destroy = function() +{ + + this.vertices = null; + this.indices = null; + + this.gl.deleteBuffer( this.vertexBuffer ); + this.gl.deleteBuffer( this.indexBuffer ); + + this.currentBaseTexture = null; + + this.gl = null; +}; + diff --git a/src/pixi/text/BitmapText.js b/src/pixi/text/BitmapText.js index fb5fb8cc..54136e7a 100644 --- a/src/pixi/text/BitmapText.js +++ b/src/pixi/text/BitmapText.js @@ -9,16 +9,18 @@ * http://www.bmglyph.com/ for mac. * * @class BitmapText - * @extends DisplayObjectContainer + * @extends SpriteBatch * @constructor * @param text {String} The copy that you would like the text to display * @param style {Object} The style parameters * @param style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) - * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right') + * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text */ PIXI.BitmapText = function(text, style) { - PIXI.DisplayObjectContainer.call(this); + PIXI.SpriteBatch.call(this); + + this._pool = []; this.setText(text); this.setStyle(style); @@ -27,7 +29,7 @@ PIXI.BitmapText = function(text, style) }; // constructor -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +PIXI.BitmapText.prototype = Object.create(PIXI.SpriteBatch.prototype); PIXI.BitmapText.prototype.constructor = PIXI.BitmapText; /** @@ -44,11 +46,11 @@ PIXI.BitmapText.prototype.setText = function(text) /** * Set the style of the text + * style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) + * [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text * * @method setStyle - * @param style {Object} The style parameters - * @param style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) - * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right') + * @param style {Object} The style parameters, contained as properties of an object */ PIXI.BitmapText.prototype.setStyle = function(style) { @@ -61,6 +63,7 @@ PIXI.BitmapText.prototype.setStyle = function(style) this.fontSize = font.length >= 2 ? parseInt(font[font.length - 2], 10) : PIXI.BitmapText.fonts[this.fontName].size; this.dirty = true; + this.tint = style.tint; }; /** @@ -79,6 +82,8 @@ PIXI.BitmapText.prototype.updateText = function() var lineWidths = []; var line = 0; var scale = this.fontSize / data.size; + + for(var i = 0; i < this.text.length; i++) { var charCode = this.text.charCodeAt(i); @@ -125,13 +130,30 @@ PIXI.BitmapText.prototype.updateText = function() lineAlignOffsets.push(alignOffset); } - for(i = 0; i < chars.length; i++) + var lenChildren = this.children.length; + var lenChars = chars.length; + var tint = this.tint || 0xFFFFFF; + for(i = 0; i < lenChars; i++) { - var c = new PIXI.Sprite(chars[i].texture); //PIXI.Sprite.fromFrame(chars[i].charCode); + var c = i < lenChildren ? this.children[i] : this._pool.pop(); // get old child if have. if not - take from pool. + + if (c) c.setTexture(chars[i].texture); // check if got one before. + else c = new PIXI.Sprite(chars[i].texture); // if no create new one. + c.position.x = (chars[i].position.x + lineAlignOffsets[chars[i].line]) * scale; c.position.y = chars[i].position.y * scale; c.scale.x = c.scale.y = scale; - this.addChild(c); + c.tint = tint; + if (!c.parent) this.addChild(c); + } + + // remove unnecessary children. + // and put their into the pool. + while(this.children.length > lenChars) + { + var child = this.getChildAt(this.children.length - 1); + this._pool.push(child); + this.removeChild(child); } this.width = maxLineWidth * scale; @@ -139,7 +161,7 @@ PIXI.BitmapText.prototype.updateText = function() }; /** - * Updates the transfor of this object + * Updates the transform of this object * * @method updateTransform * @private @@ -148,16 +170,11 @@ PIXI.BitmapText.prototype.updateTransform = function() { if(this.dirty) { - while(this.children.length > 0) - { - this.removeChild(this.getChildAt(0)); - } this.updateText(); - this.dirty = false; } - PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); + PIXI.SpriteBatch.prototype.updateTransform.call(this); }; PIXI.BitmapText.fonts = {}; diff --git a/src/pixi/text/Text.js b/src/pixi/text/Text.js index 75d50d05..5244ca24 100644 --- a/src/pixi/text/Text.js +++ b/src/pixi/text/Text.js @@ -3,7 +3,7 @@ */ /** - * A Text Object will create a line(s) of text to split a line you can use '\n' + * A Text Object will create a line(s) of text. To split a line you can use '\n' * * @class Text * @extends Sprite @@ -12,7 +12,7 @@ * @param [style] {Object} The style parameters * @param [style.font] {String} default 'bold 20pt Arial' The style and size of the font * @param [style.fill='black'] {Object} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' - * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right') + * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text * @param [style.stroke] {String} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used @@ -20,8 +20,21 @@ */ PIXI.Text = function(text, style) { + /** + * The canvas element that everything is drawn to + * + * @property canvas + * @type HTMLCanvasElement + */ this.canvas = document.createElement('canvas'); + + /** + * The canvas 2d context that everything is drawn with + * @property context + * @type HTMLCanvasElement 2d Context + */ this.context = this.canvas.getContext('2d'); + PIXI.Sprite.call(this, PIXI.Texture.fromCanvas(this.canvas)); this.setText(text); @@ -42,7 +55,7 @@ PIXI.Text.prototype.constructor = PIXI.Text; * @param [style] {Object} The style parameters * @param [style.font='bold 20pt Arial'] {String} The style and size of the font * @param [style.fill='black'] {Object} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' - * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right') + * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text * @param [style.stroke='black'] {String} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used @@ -72,6 +85,7 @@ PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || ' '; this.dirty = true; + }; /** @@ -108,6 +122,8 @@ PIXI.Text.prototype.updateText = function() var lineHeight = this.determineFontHeight('font: ' + this.style.font + ';') + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; + if(navigator.isCocoonJS) this.context.clearRect(0,0,this.canvas.width,this.canvas.height); + //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; @@ -161,11 +177,29 @@ PIXI.Text.prototype.updateTexture = function() this._width = this.canvas.width; this._height = this.canvas.height; - PIXI.texturesToUpdate.push(this.texture.baseTexture); + this.requiresUpdate = true; }; /** - * Updates the transfor of this object +* Renders the object using the WebGL renderer +* +* @method _renderWebGL +* @param renderSession {RenderSession} +* @private +*/ +PIXI.Text.prototype._renderWebGL = function(renderSession) +{ + if(this.requiresUpdate) + { + this.requiresUpdate = false; + PIXI.updateWebGLTexture(this.texture.baseTexture, renderSession.gl); + } + + PIXI.Sprite.prototype._renderWebGL.call(this, renderSession); +}; + +/** + * Updates the transform of this object * * @method updateTransform * @private @@ -184,6 +218,7 @@ PIXI.Text.prototype.updateTransform = function() /* * http://stackoverflow.com/users/34441/ellisbben * great solution to the problem! + * returns the height of the given font * * @method determineFontHeight * @param fontStyle {Object} diff --git a/src/pixi/textures/BaseTexture.js b/src/pixi/textures/BaseTexture.js index 1c78656d..ae0b4c71 100644 --- a/src/pixi/textures/BaseTexture.js +++ b/src/pixi/textures/BaseTexture.js @@ -6,6 +6,8 @@ PIXI.BaseTextureCache = {}; PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; +PIXI.BaseTextureCacheIdGenerator = 0; + /** * A texture stores the information that represents an image. All textures have a base texture * @@ -13,6 +15,7 @@ PIXI.texturesToDestroy = []; * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) + * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts */ PIXI.BaseTexture = function(source, scaleMode) { @@ -39,10 +42,10 @@ PIXI.BaseTexture = function(source, scaleMode) /** * The scale mode to apply when scaling this texture * @property scaleMode - * @type PIXI.BaseTexture.SCALE_MODE - * @default PIXI.BaseTexture.SCALE_MODE.LINEAR + * @type PIXI.scaleModes + * @default PIXI.scaleModes.LINEAR */ - this.scaleMode = scaleMode || PIXI.BaseTexture.SCALE_MODE.DEFAULT; + this.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; /** * [read-only] Describes if the base texture has loaded or not @@ -63,34 +66,7 @@ PIXI.BaseTexture = function(source, scaleMode) if(!source)return; - if(this.source instanceof Image || this.source instanceof HTMLImageElement) - { - if(this.source.complete) - { - this.hasLoaded = true; - this.width = this.source.width; - this.height = this.source.height; - - PIXI.texturesToUpdate.push(this); - } - else - { - - var scope = this; - this.source.onload = function() { - - scope.hasLoaded = true; - scope.width = scope.source.width; - scope.height = scope.source.height; - - // add it to somewhere... - PIXI.texturesToUpdate.push(scope); - scope.dispatchEvent( { type: 'loaded', content: scope } ); - }; - //this.image.src = imageUrl; - } - } - else + if(this.source.complete || this.source.getContext) { this.hasLoaded = true; this.width = this.source.width; @@ -98,9 +74,32 @@ PIXI.BaseTexture = function(source, scaleMode) PIXI.texturesToUpdate.push(this); } + else + { + + var scope = this; + this.source.onload = function() { + + scope.hasLoaded = true; + scope.width = scope.source.width; + scope.height = scope.source.height; + + // add it to somewhere... + PIXI.texturesToUpdate.push(scope); + scope.dispatchEvent( { type: 'loaded', content: scope } ); + }; + //this.image.src = imageUrl; + } this.imageUrl = null; this._powerOf2 = false; + + //TODO will be used for futer pixi 1.5... + this.id = PIXI.BaseTextureCacheIdGenerator++; + + // used for webGL + this._glTextures = []; + }; PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; @@ -112,10 +111,9 @@ PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; */ PIXI.BaseTexture.prototype.destroy = function() { - if(this.source instanceof Image) + if(this.imageUrl) { - if (this.imageUrl in PIXI.BaseTextureCache) - delete PIXI.BaseTextureCache[this.imageUrl]; + delete PIXI.BaseTextureCache[this.imageUrl]; this.imageUrl = null; this.source.src = null; } @@ -138,7 +136,7 @@ PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc) /** * Helper function that returns a base texture based on an image url - * If the image is not in the base texture cache it will be created and loaded + * If the image is not in the base texture cache it will be created and loaded * * @static * @method fromImage @@ -148,6 +146,8 @@ PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc) PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode) { var baseTexture = PIXI.BaseTextureCache[imageUrl]; + crossorigin = !crossorigin; + if(!baseTexture) { // new Image() breaks tex loading in some versions of Chrome. @@ -166,8 +166,22 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode) return baseTexture; }; -PIXI.BaseTexture.SCALE_MODE = { - DEFAULT: 0, //default to LINEAR - LINEAR: 0, - NEAREST: 1 -}; \ No newline at end of file +PIXI.BaseTexture.fromCanvas = function(canvas, scaleMode) +{ + if(!canvas._pixiId) + { + canvas._pixiId = 'canvas_' + PIXI.TextureCacheIdGenerator++; + } + + var baseTexture = PIXI.BaseTextureCache[canvas._pixiId]; + + if(!baseTexture) + { + baseTexture = new PIXI.BaseTexture(canvas, scaleMode); + PIXI.BaseTextureCache[canvas._pixiId] = baseTexture; + } + + return baseTexture; +}; + + diff --git a/src/pixi/textures/RenderTexture.js b/src/pixi/textures/RenderTexture.js index fe1df380..602d68e1 100644 --- a/src/pixi/textures/RenderTexture.js +++ b/src/pixi/textures/RenderTexture.js @@ -5,7 +5,7 @@ /** A RenderTexture is a special texture that allows any pixi displayObject to be rendered to it. - __Hint__: All DisplayObjects (exmpl. Sprites) that renders on RenderTexture should be preloaded. + __Hint__: All DisplayObjects (exmpl. Sprites) that render on RenderTexture should be preloaded. Otherwise black rectangles will be drawn instead. RenderTexture takes snapshot of DisplayObject passed to render method. If DisplayObject is passed to render method, position and rotation of it will be ignored. For example: @@ -30,112 +30,73 @@ @param width {Number} The width of the render texture @param height {Number} The height of the render texture */ -PIXI.RenderTexture = function(width, height) +PIXI.RenderTexture = function(width, height, renderer) { PIXI.EventTarget.call( this ); this.width = width || 100; this.height = height || 100; - this.indetityMatrix = PIXI.mat3.create(); - this.frame = new PIXI.Rectangle(0, 0, this.width, this.height); - if(PIXI.gl) + this.baseTexture = new PIXI.BaseTexture(); + this.baseTexture.width = this.width; + this.baseTexture.height = this.height; + this.baseTexture._glTextures = []; + + this.baseTexture.hasLoaded = true; + + // each render texture can only belong to one renderer at the moment if its webGL + this.renderer = renderer || PIXI.defaultRenderer; + + if(this.renderer.type === PIXI.WEBGL_RENDERER) { - this.initWebGL(); + var gl = this.renderer.gl; + + this.textureBuffer = new PIXI.FilterTexture(gl, this.width, this.height); + this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; + + this.render = this.renderWebGL; + this.projection = new PIXI.Point(this.width/2 , -this.height/2); } else { - this.initCanvas(); + this.render = this.renderCanvas; + this.textureBuffer = new PIXI.CanvasBuffer(this.width, this.height); + this.baseTexture.source = this.textureBuffer.canvas; } + + PIXI.Texture.frameUpdates.push(this); + + }; PIXI.RenderTexture.prototype = Object.create( PIXI.Texture.prototype ); PIXI.RenderTexture.prototype.constructor = PIXI.RenderTexture; -/** - * Initializes the webgl data for this texture - * - * @method initWebGL - * @private - */ -PIXI.RenderTexture.prototype.initWebGL = function() -{ - var gl = PIXI.gl; - this.glFramebuffer = gl.createFramebuffer(); - - gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer ); - - this.glFramebuffer.width = this.width; - this.glFramebuffer.height = this.height; - - this.baseTexture = new PIXI.BaseTexture(); - - this.baseTexture.width = this.width; - this.baseTexture.height = this.height; - - this.baseTexture._glTexture = gl.createTexture(); - gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTexture); - - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - - this.baseTexture.isRender = true; - - gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer ); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.baseTexture._glTexture, 0); - - // create a projection matrix.. - this.projection = new PIXI.Point(this.width/2 , -this.height/2); - - // set the correct render function.. - this.render = this.renderWebGL; -}; - - PIXI.RenderTexture.prototype.resize = function(width, height) { - this.width = width; this.height = height; - if(PIXI.gl) + this.frame.width = this.width; + this.frame.height = this.height; + + if(this.renderer.type === PIXI.WEBGL_RENDERER) { this.projection.x = this.width / 2; this.projection.y = -this.height / 2; - var gl = PIXI.gl; - gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTexture); + var gl = this.gl; + gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTextures[gl.id]); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); } else { - - this.frame.width = this.width; - this.frame.height = this.height; - this.renderer.resize(this.width, this.height); + this.textureBuffer.resize(this.width, this.height); } -}; -/** - * Initializes the canvas data for this texture - * - * @method initCanvas - * @private - */ -PIXI.RenderTexture.prototype.initCanvas = function() -{ - this.renderer = new PIXI.CanvasRenderer(this.width, this.height, null, 0); - - this.baseTexture = new PIXI.BaseTexture(this.renderer.view); - this.frame = new PIXI.Rectangle(0, 0, this.width, this.height); - - this.render = this.renderCanvas; + PIXI.Texture.frameUpdates.push(this); }; /** @@ -148,64 +109,42 @@ PIXI.RenderTexture.prototype.initCanvas = function() */ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, clear) { - var gl = PIXI.gl; + var gl = this.renderer.gl; - // enable the alpha color mask.. gl.colorMask(true, true, true, true); gl.viewport(0, 0, this.width, this.height); - gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer ); + gl.bindFramebuffer(gl.FRAMEBUFFER, this.textureBuffer.frameBuffer ); - if(clear) - { - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - } + if(clear)this.textureBuffer.clear(); // THIS WILL MESS WITH HIT TESTING! var children = displayObject.children; //TODO -? create a new one??? dont think so! var originalWorldTransform = displayObject.worldTransform; - displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; + displayObject.worldTransform = PIXI.RenderTexture.tempMatrix; // modify to flip... - displayObject.worldTransform[4] = -1; - displayObject.worldTransform[5] = this.projection.y * -2; + displayObject.worldTransform.d = -1; + displayObject.worldTransform.ty = this.projection.y * -2; if(position) { - displayObject.worldTransform[2] = position.x; - displayObject.worldTransform[5] -= position.y; + displayObject.worldTransform.tx = position.x; + displayObject.worldTransform.ty -= position.y; } - PIXI.visibleCount++; - displayObject.vcount = PIXI.visibleCount; - for(var i=0,j=children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; }; +/** + * Converts a color as an [R, G, B] array to a hex number + * + * @method rgb2hex + * @param rgb {Array} + */ +PIXI.rgb2hex = function(rgb) { + return ((rgb[0]*255 << 16) + (rgb[1]*255 << 8) + rgb[2]*255); +}; + /** * A polyfill for Function.prototype.bind * @@ -86,7 +102,7 @@ if (typeof Function.prototype.bind !== 'function') { * @class AjaxRequest * @constructor */ -PIXI.AjaxRequest = function AjaxRequest() +PIXI.AjaxRequest = function() { var activexmodes = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.3.0', 'Microsoft.XMLHTTP']; //activeX versions to check for in IE @@ -111,34 +127,71 @@ PIXI.AjaxRequest = function AjaxRequest() return false; } }; - /* - * DEBUGGING ONLY - */ -PIXI.runList = function(item) +PIXI.packColorRGBA = function(r, g, b, a)//r, g, b, a) { - window.console.log('>>>>>>>>>'); - window.console.log('_'); - var safe = 0; - var tmp = item.first; - window.console.log(tmp); + // console.log(r, b, c, d) + return (Math.floor((r)*63) << 18) | (Math.floor((g)*63) << 12) | (Math.floor((b)*63) << 6);// | (Math.floor((a)*63)) + // i = i | (Math.floor((a)*63)); + // return i; + // var r = (i / 262144.0 ) / 64; + // var g = (i / 4096.0)%64 / 64; + // var b = (i / 64.0)%64 / 64; + // var a = (i)%64 / 64; + + // console.log(r, g, b, a); + // return i; - while(tmp._iNext) - { - safe++; - tmp = tmp._iNext; - window.console.log(tmp); - // console.log(tmp); - - if(safe > 100) - { - window.console.log('BREAK'); - break; - } - } +}; +*/ +/* +PIXI.packColorRGB = function(r, g, b)//r, g, b, a) +{ + return (Math.floor((r)*255) << 16) | (Math.floor((g)*255) << 8) | (Math.floor((b)*255)); }; +PIXI.unpackColorRGB = function(r, g, b)//r, g, b, a) +{ + return (Math.floor((r)*255) << 16) | (Math.floor((g)*255) << 8) | (Math.floor((b)*255)); +}; +*/ +/** + * Checks whether the Canvas BlendModes are supported by the current browser + * + * @method canUseNewCanvasBlendModes + * @return {Boolean} whether they are supported + */ +PIXI.canUseNewCanvasBlendModes = function() +{ + var canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + var context = canvas.getContext('2d'); + context.fillStyle = '#000'; + context.fillRect(0,0,1,1); + context.globalCompositeOperation = 'multiply'; + context.fillStyle = '#fff'; + context.fillRect(0,0,1,1); + return context.getImageData(0,0,1,1).data[0] === 0; +}; - - +/** + * Given a number, this function returns the closest number that is a power of two + * this function is taken from Starling Framework as its pretty neat ;) + * + * @method getNextPowerOfTwo + * @param number {Number} + * @return {Number} the closest number that is a power of two + */ +PIXI.getNextPowerOfTwo = function(number) +{ + if (number > 0 && (number & (number - 1)) === 0) // see: http://goo.gl/D9kPj + return number; + else + { + var result = 1; + while (result < number) result <<= 1; + return result; + } +};