From fc80c0c602a1a5757a173592ee2d8ef0f4682e9f Mon Sep 17 00:00:00 2001 From: Mat Groves Date: Mon, 30 Sep 2013 01:19:21 +0100 Subject: [PATCH] FIlters Added.. again! some filters added to pixi example 15 added too --- Gruntfile.js | 6 +- bin/pixi.dev.js | 1765 +-- bin/pixi.js | 10 +- examples/example 1 - Basics/pixi.js | 1765 +-- examples/example 10 - Text/pixi.js | 1765 +-- examples/example 11 - RenderTexture/pixi.js | 1765 +-- examples/example 12 - Spine/pixi.js | 1765 +-- examples/example 13 - Graphics/pixi.js | 1765 +-- examples/example 14 - Masking/pixi.js | 1765 +-- examples/example 15 - Filters/BGrotate.jpg | Bin 0 -> 139735 bytes .../example 15 - Filters/LightRotate1.png | Bin 0 -> 213699 bytes .../example 15 - Filters/LightRotate2.png | Bin 0 -> 173189 bytes examples/example 15 - Filters/SceneRotate.jpg | Bin 0 -> 113742 bytes examples/example 15 - Filters/index.html | 159 + examples/example 15 - Filters/panda.png | Bin 0 -> 69177 bytes examples/example 15 - Filters/pixi.js | 10649 ++++++++++++++++ examples/example 2 - SpriteSheet/pixi.js | 1765 +-- examples/example 3 - MovieClip/pixi.js | 1765 +-- examples/example 4 - Balls/pixi.js | 1765 +-- examples/example 5 - Morph/pixi.js | 1765 +-- examples/example 6 - Interactivity/pixi.js | 1765 +-- .../pixi.js | 1765 +-- examples/example 8 - Dragging/pixi.js | 1765 +-- examples/example 9 - Tiling Texture/pixi.js | 1765 +-- src/pixi/InteractionManager.js | 188 +- src/pixi/display/DisplayObject.js | 177 +- src/pixi/display/DisplayObjectContainer.js | 112 +- src/pixi/display/Sprite.js | 17 +- src/pixi/display/Stage.js | 12 +- src/pixi/filters/ColorMatrixFilter.js | 38 + src/pixi/filters/FilterBlock.js | 6 +- src/pixi/filters/GreyFilter.js | 47 + src/pixi/filters/InvertFilter.js | 36 + src/pixi/filters/SepiaFilter.js | 38 + src/pixi/renderers/canvas/CanvasRenderer.js | 163 +- src/pixi/renderers/webgl/PixiShader.js | 70 + src/pixi/renderers/webgl/WebGLBatch.js | 48 +- src/pixi/renderers/webgl/WebGLGraphics.js | 240 +- src/pixi/renderers/webgl/WebGLRenderGroup.js | 371 +- src/pixi/renderers/webgl/WebGLRenderer.js | 78 +- src/pixi/renderers/webgl/WebGLShaders.js | 121 +- src/pixi/textures/RenderTexture.js | 55 +- 42 files changed, 26689 insertions(+), 12427 deletions(-) create mode 100644 examples/example 15 - Filters/BGrotate.jpg create mode 100644 examples/example 15 - Filters/LightRotate1.png create mode 100644 examples/example 15 - Filters/LightRotate2.png create mode 100644 examples/example 15 - Filters/SceneRotate.jpg create mode 100644 examples/example 15 - Filters/index.html create mode 100644 examples/example 15 - Filters/panda.png create mode 100644 examples/example 15 - Filters/pixi.js create mode 100644 src/pixi/filters/ColorMatrixFilter.js create mode 100644 src/pixi/filters/GreyFilter.js create mode 100644 src/pixi/filters/InvertFilter.js create mode 100644 src/pixi/filters/SepiaFilter.js create mode 100644 src/pixi/renderers/webgl/PixiShader.js diff --git a/Gruntfile.js b/Gruntfile.js index b083b49..5511724 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -22,6 +22,8 @@ module.exports = function(grunt) { '<%= dirs.src %>/display/Sprite.js', '<%= dirs.src %>/display/MovieClip.js', '<%= dirs.src %>/filters/FilterBlock.js', + '<%= dirs.src %>/filters/ColorMatrixFilter.js', + '<%= dirs.src %>/filters/GreyFilter.js', '<%= dirs.src %>/text/Text.js', '<%= dirs.src %>/text/BitmapText.js', '<%= dirs.src %>/InteractionManager.js', @@ -31,6 +33,7 @@ module.exports = function(grunt) { '<%= dirs.src %>/utils/Detector.js', '<%= dirs.src %>/utils/Polyk.js', '<%= dirs.src %>/renderers/webgl/WebGLShaders.js', + '<%= dirs.src %>/renderers/webgl/PixiShader.js', '<%= dirs.src %>/renderers/webgl/WebGLGraphics.js', '<%= dirs.src %>/renderers/webgl/WebGLRenderer.js', '<%= dirs.src %>/renderers/webgl/WebGLBatch.js', @@ -124,7 +127,8 @@ module.exports = function(grunt) { 'examples/example 11 - RenderTexture', 'examples/example 12 - Spine', 'examples/example 13 - Graphics', - 'examples/example 14 - Masking' + 'examples/example 14 - Masking', + 'examples/example 15 - Filters' ] }, connect: { diff --git a/bin/pixi.dev.js b/bin/pixi.dev.js index a6e7055..6d567a2 100644 --- a/bin/pixi.dev.js +++ b/bin/pixi.dev.js @@ -4,7 +4,7 @@ * Copyright (c) 2012, Mat Groves * http://goodboydigital.com/ * - * Compiled: 2013-07-24 + * Compiled: 2013-09-30 * * Pixi.JS is licensed under the MIT License. * http://www.opensource.org/licenses/mit-license.php @@ -34,19 +34,19 @@ var PIXI = PIXI || {}; * The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis. * * @class Point - * @constructor + * @constructor * @param x {Number} position of the point * @param y {Number} position of the point */ PIXI.Point = function(x, y) { /** - * @property x + * @property x * @type Number * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -78,10 +78,10 @@ PIXI.Point.prototype.constructor = PIXI.Point; * the Rectangle object is an area defined by its position, as indicated by its top-left corner point (x, y) and by its width and its height. * * @class Rectangle - * @constructor + * @constructor * @param x {Number} The X coord of the upper-left corner of the rectangle * @param y {Number} The Y coord of the upper-left corner of the rectangle - * @param width {Number} The overall wisth of this rectangle + * @param width {Number} The overall width of this rectangle * @param height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) @@ -92,21 +92,21 @@ PIXI.Rectangle = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -140,11 +140,11 @@ PIXI.Rectangle.prototype.contains = function(x, y) return false; var x1 = this.x; - if(x > x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y>16)/255,(255&t>>8)/255,(255&t)/255]}function e(t){return[(255&t>>16)/255,(255&t>>8)/255,(255&t)/255]}var i=this,n=n||{};n.Point=function(t,e){this.x=t||0,this.y=e||0},n.Point.prototype.clone=function(){return new n.Point(this.x,this.y)},n.Point.prototype.constructor=n.Point,n.Rectangle=function(t,e,i,r){this.x=t||0,this.y=e||0,this.width=i||0,this.height=r||0},n.Rectangle.prototype.clone=function(){return new n.Rectangle(this.x,this.y,this.width,this.height)},n.Rectangle.prototype.contains=function(t,e){if(0>=this.width||0>=this.height)return!1;var i=this.x;if(t>i&&i+this.width>t){var r=this.y;if(e>r&&r+this.height>e)return!0}return!1},n.Rectangle.prototype.constructor=n.Rectangle,n.Polygon=function(t){if(t instanceof Array||(t=Array.prototype.slice.call(arguments)),"number"==typeof t[0]){for(var e=[],i=0,r=t.length;r>i;i+=2)e.push(new n.Point(t[i],t[i+1]));t=e}this.points=t},n.Polygon.prototype.clone=function(){for(var t=[],e=0;this.points.length>e;e++)t.push(this.points[e].clone());return new n.Polygon(t)},n.Polygon.prototype.contains=function(t,e){for(var i=!1,r=0,n=this.points.length-1;this.points.length>r;n=r++){var s=this.points[r].x,o=this.points[r].y,a=this.points[n].x,h=this.points[n].y,l=o>e!=h>e&&(a-s)*(e-o)/(h-o)+s>t;l&&(i=!i)}return i},n.Polygon.prototype.constructor=n.Polygon,n.Circle=function(t,e,i){this.x=t||0,this.y=e||0,this.radius=i||0},n.Circle.prototype.clone=function(){return new n.Circle(this.x,this.y,this.radius)},n.Circle.prototype.contains=function(t,e){if(0>=this.radius)return!1;var i=this.x-t,r=this.y-e,n=this.radius*this.radius;return i*=i,r*=r,n>=i+r},n.Circle.prototype.constructor=n.Circle,n.Ellipse=function(t,e,i,r){this.x=t||0,this.y=e||0,this.width=i||0,this.height=r||0},n.Ellipse.prototype.clone=function(){return new n.Ellipse(this.x,this.y,this.width,this.height)},n.Ellipse.prototype.contains=function(t,e){if(0>=this.width||0>=this.height)return!1;var i=(t-this.x)/this.width-.5,r=(e-this.y)/this.height-.5;return i*=i,r*=r,.25>i+r},n.Ellipse.getBounds=function(){return new n.Rectangle(this.x,this.y,this.width,this.height)},n.Ellipse.prototype.constructor=n.Ellipse,t(),n.mat3={},n.mat3.create=function(){var t=new n.Matrix(9);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.mat3.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.mat4={},n.mat4.create=function(){var t=new n.Matrix(16);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.mat3.multiply=function(t,e,i){i||(i=t);var r=t[0],n=t[1],s=t[2],o=t[3],a=t[4],h=t[5],l=t[6],u=t[7],c=t[8],p=e[0],d=e[1],f=e[2],v=e[3],m=e[4],g=e[5],x=e[6],b=e[7],y=e[8];return i[0]=p*r+d*o+f*l,i[1]=p*n+d*a+f*u,i[2]=p*s+d*h+f*c,i[3]=v*r+m*o+g*l,i[4]=v*n+m*a+g*u,i[5]=v*s+m*h+g*c,i[6]=x*r+b*o+y*l,i[7]=x*n+b*a+y*u,i[8]=x*s+b*h+y*c,i},n.mat3.clone=function(t){var e=new n.Matrix(9);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e[4]=t[4],e[5]=t[5],e[6]=t[6],e[7]=t[7],e[8]=t[8],e},n.mat3.transpose=function(t,e){if(!e||t===e){var i=t[1],r=t[2],n=t[5];return t[1]=t[3],t[2]=t[6],t[3]=i,t[5]=t[7],t[6]=r,t[7]=n,t}return e[0]=t[0],e[1]=t[3],e[2]=t[6],e[3]=t[1],e[4]=t[4],e[5]=t[7],e[6]=t[2],e[7]=t[5],e[8]=t[8],e},n.mat3.toMat4=function(t,e){return e||(e=n.mat4.create()),e[15]=1,e[14]=0,e[13]=0,e[12]=0,e[11]=0,e[10]=t[8],e[9]=t[7],e[8]=t[6],e[7]=0,e[6]=t[5],e[5]=t[4],e[4]=t[3],e[3]=0,e[2]=t[2],e[1]=t[1],e[0]=t[0],e},n.mat4.create=function(){var t=new n.Matrix(16);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.mat4.transpose=function(t,e){if(!e||t===e){var i=t[1],r=t[2],n=t[3],s=t[6],o=t[7],a=t[11];return t[1]=t[4],t[2]=t[8],t[3]=t[12],t[4]=i,t[6]=t[9],t[7]=t[13],t[8]=r,t[9]=s,t[11]=t[14],t[12]=n,t[13]=o,t[14]=a,t}return e[0]=t[0],e[1]=t[4],e[2]=t[8],e[3]=t[12],e[4]=t[1],e[5]=t[5],e[6]=t[9],e[7]=t[13],e[8]=t[2],e[9]=t[6],e[10]=t[10],e[11]=t[14],e[12]=t[3],e[13]=t[7],e[14]=t[11],e[15]=t[15],e},n.mat4.multiply=function(t,e,i){i||(i=t);var r=t[0],n=t[1],s=t[2],o=t[3],a=t[4],h=t[5],l=t[6],u=t[7],c=t[8],p=t[9],d=t[10],f=t[11],v=t[12],m=t[13],g=t[14],x=t[15],b=e[0],y=e[1],T=e[2],_=e[3];return i[0]=b*r+y*a+T*c+_*v,i[1]=b*n+y*h+T*p+_*m,i[2]=b*s+y*l+T*d+_*g,i[3]=b*o+y*u+T*f+_*x,b=e[4],y=e[5],T=e[6],_=e[7],i[4]=b*r+y*a+T*c+_*v,i[5]=b*n+y*h+T*p+_*m,i[6]=b*s+y*l+T*d+_*g,i[7]=b*o+y*u+T*f+_*x,b=e[8],y=e[9],T=e[10],_=e[11],i[8]=b*r+y*a+T*c+_*v,i[9]=b*n+y*h+T*p+_*m,i[10]=b*s+y*l+T*d+_*g,i[11]=b*o+y*u+T*f+_*x,b=e[12],y=e[13],T=e[14],_=e[15],i[12]=b*r+y*a+T*c+_*v,i[13]=b*n+y*h+T*p+_*m,i[14]=b*s+y*l+T*d+_*g,i[15]=b*o+y*u+T*f+_*x,i},n.DisplayObject=function(){this.last=this,this.first=this,this.position=new n.Point,this.scale=new n.Point(1,1),this.pivot=new n.Point(0,0),this.rotation=0,this.alpha=1,this.visible=!0,this.hitArea=null,this.buttonMode=!1,this.renderable=!1,this.worldVisible=!1,this.parent=null,this.stage=null,this.childIndex=0,this.worldAlpha=1,this._interactive=!1,this.worldTransform=n.mat3.create(),this.localTransform=n.mat3.create(),this.color=[],this.dynamic=!0,this._sr=0,this._cr=1},n.DisplayObject.prototype.constructor=n.DisplayObject,n.DisplayObject.prototype.setInteractive=function(t){this.interactive=t},Object.defineProperty(n.DisplayObject.prototype,"interactive",{get:function(){return this._interactive},set:function(t){this._interactive=t,this.stage&&(this.stage.dirty=!0)}}),Object.defineProperty(n.DisplayObject.prototype,"mask",{get:function(){return this._mask},set:function(t){this._mask=t,t?this.addFilter(t):this.removeFilter()}}),n.DisplayObject.prototype.addFilter=function(t){if(!this.filter){this.filter=!0;var e=new n.FilterBlock,i=new n.FilterBlock;e.mask=t,i.mask=t,e.first=e.last=this,i.first=i.last=this,e.open=!0;var r,s,o=e,a=e;s=this.first._iPrev,s?(r=s._iNext,o._iPrev=s,s._iNext=o):r=this,r&&(r._iPrev=a,a._iNext=r);var o=i,a=i,r=null,s=null;s=this.last,r=s._iNext,r&&(r._iPrev=a,a._iNext=r),o._iPrev=s,s._iNext=o;for(var h=this,l=this.last;h;)h.last==l&&(h.last=i),h=h.parent;this.first=e,this.__renderGroup&&this.__renderGroup.addFilterBlocks(e,i),t.renderable=!1}},n.DisplayObject.prototype.removeFilter=function(){if(this.filter){this.filter=!1;var t=this.first,e=t._iNext,i=t._iPrev;e&&(e._iPrev=i),i&&(i._iNext=e),this.first=t._iNext;var r=this.last,e=r._iNext,i=r._iPrev;e&&(e._iPrev=i),i._iNext=e;for(var n=r._iPrev,s=this;s.last==r&&(s.last=n,s=s.parent););var o=t.mask;o.renderable=!0,this.__renderGroup&&this.__renderGroup.removeFilterBlocks(t,r)}},n.DisplayObject.prototype.updateTransform=function(){this.rotation!=this.rotationCache&&(this.rotationCache=this.rotation,this._sr=Math.sin(this.rotation),this._cr=Math.cos(this.rotation));var t=this.localTransform,e=this.parent.worldTransform,i=this.worldTransform;t[0]=this._cr*this.scale.x,t[1]=-this._sr*this.scale.y,t[3]=this._sr*this.scale.x,t[4]=this._cr*this.scale.y;var r=this.pivot.x,n=this.pivot.y,s=t[0],o=t[1],a=this.position.x-t[0]*r-n*t[1],h=t[3],l=t[4],u=this.position.y-t[4]*n-r*t[3],c=e[0],p=e[1],d=e[2],f=e[3],v=e[4],m=e[5];t[2]=a,t[5]=u,i[0]=c*s+p*h,i[1]=c*o+p*l,i[2]=c*a+p*u+d,i[3]=f*s+v*h,i[4]=f*o+v*l,i[5]=f*a+v*u+m,this.worldAlpha=this.alpha*this.parent.worldAlpha},n.DisplayObjectContainer=function(){n.DisplayObject.call(this),this.children=[]},n.DisplayObjectContainer.prototype=Object.create(n.DisplayObject.prototype),n.DisplayObjectContainer.prototype.constructor=n.DisplayObjectContainer,n.DisplayObjectContainer.prototype.addChild=function(t){if(void 0!=t.parent&&t.parent.removeChild(t),t.parent=this,t.childIndex=this.children.length,this.children.push(t),this.stage){var e=t;do e.interactive&&(this.stage.dirty=!0),e.stage=this.stage,e=e._iNext;while(e)}var i,r,n=t.first,s=t.last;r=this.filter?this.last._iPrev:this.last,i=r._iNext;for(var o=this,a=r;o;)o.last==a&&(o.last=t.last),o=o.parent;i&&(i._iPrev=s,s._iNext=i),n._iPrev=r,r._iNext=n,this.__renderGroup&&(t.__renderGroup&&t.__renderGroup.removeDisplayObjectAndChildren(t),this.__renderGroup.addDisplayObjectAndChildren(t))},n.DisplayObjectContainer.prototype.addChildAt=function(t,e){if(!(e>=0&&this.children.length>=e))throw Error(t+" The index "+e+" supplied is out of bounds "+this.children.length);if(void 0!=t.parent&&t.parent.removeChild(t),t.parent=this,this.stage){var i=t;do i.interactive&&(this.stage.dirty=!0),i.stage=this.stage,i=i._iNext;while(i)}var r,n,s=t.first,o=t.last;if(e==this.children.length){n=this.last;for(var a=this,h=this.last;a;)a.last==h&&(a.last=t.last),a=a.parent}else n=0==e?this:this.children[e-1].last;r=n._iNext,r&&(r._iPrev=o,o._iNext=r),s._iPrev=n,n._iNext=s,this.children.splice(e,0,t),this.__renderGroup&&(t.__renderGroup&&t.__renderGroup.removeDisplayObjectAndChildren(t),this.__renderGroup.addDisplayObjectAndChildren(t))},n.DisplayObjectContainer.prototype.swapChildren=function(){},n.DisplayObjectContainer.prototype.getChildAt=function(t){if(t>=0&&this.children.length>t)return this.children[t];throw Error(child+" Both the supplied DisplayObjects must be a child of the caller "+this)},n.DisplayObjectContainer.prototype.removeChild=function(t){var e=this.children.indexOf(t);if(-1===e)throw Error(t+" The supplied DisplayObject must be a child of the caller "+this);var i=t.first,r=t.last,n=r._iNext,s=i._iPrev;if(n&&(n._iPrev=s),s._iNext=n,this.last==r)for(var o=i._iPrev,a=this;a.last==r.last&&(a.last=o,a=a.parent););if(r._iNext=null,i._iPrev=null,this.stage){var h=t;do h.interactive&&(this.stage.dirty=!0),h.stage=null,h=h._iNext;while(h)}t.__renderGroup&&t.__renderGroup.removeDisplayObjectAndChildren(t),t.parent=void 0,this.children.splice(e,1)},n.DisplayObjectContainer.prototype.updateTransform=function(){if(this.visible){n.DisplayObject.prototype.updateTransform.call(this);for(var t=0,e=this.children.length;e>t;t++)this.children[t].updateTransform()}},n.blendModes={},n.blendModes.NORMAL=0,n.blendModes.SCREEN=1,n.Sprite=function(t){n.DisplayObjectContainer.call(this),this.anchor=new n.Point,this.texture=t,this.blendMode=n.blendModes.NORMAL,this._width=0,this._height=0,t.baseTexture.hasLoaded?this.updateFrame=!0:(this.onTextureUpdateBind=this.onTextureUpdate.bind(this),this.texture.addEventListener("update",this.onTextureUpdateBind)),this.renderable=!0},n.Sprite.prototype=Object.create(n.DisplayObjectContainer.prototype),n.Sprite.prototype.constructor=n.Sprite,Object.defineProperty(n.Sprite.prototype,"width",{get:function(){return this.scale.x*this.texture.frame.width},set:function(t){this.scale.x=t/this.texture.frame.width,this._width=t}}),Object.defineProperty(n.Sprite.prototype,"height",{get:function(){return this.scale.y*this.texture.frame.height},set:function(t){this.scale.y=t/this.texture.frame.height,this._height=t}}),n.Sprite.prototype.setTexture=function(t){this.texture.baseTexture!=t.baseTexture&&(this.textureChange=!0),this.texture=t,this.updateFrame=!0},n.Sprite.prototype.onTextureUpdate=function(){this._width&&(this.scale.x=this._width/this.texture.frame.width),this._height&&(this.scale.y=this._height/this.texture.frame.height),this.updateFrame=!0},n.Sprite.fromFrame=function(t){var e=n.TextureCache[t];if(!e)throw Error("The frameId '"+t+"' does not exist in the texture cache"+this);return new n.Sprite(e)},n.Sprite.fromImage=function(t){var e=n.Texture.fromImage(t);return new n.Sprite(e)},n.MovieClip=function(t){n.Sprite.call(this,t[0]),this.textures=t,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.currentFrame=0,this.playing=!1},n.MovieClip.prototype=Object.create(n.Sprite.prototype),n.MovieClip.prototype.constructor=n.MovieClip,n.MovieClip.prototype.stop=function(){this.playing=!1},n.MovieClip.prototype.play=function(){this.playing=!0},n.MovieClip.prototype.gotoAndStop=function(t){this.playing=!1,this.currentFrame=t;var e=0|this.currentFrame+.5;this.setTexture(this.textures[e%this.textures.length])},n.MovieClip.prototype.gotoAndPlay=function(t){this.currentFrame=t,this.playing=!0},n.MovieClip.prototype.updateTransform=function(){if(n.Sprite.prototype.updateTransform.call(this),this.playing){this.currentFrame+=this.animationSpeed;var t=0|this.currentFrame+.5;this.loop||this.textures.length>t?this.setTexture(this.textures[t%this.textures.length]):t>=this.textures.length&&(this.gotoAndStop(this.textures.length-1),this.onComplete&&this.onComplete())}},n.FilterBlock=function(t){this.graphics=t,this.visible=!0,this.renderable=!0},n.Text=function(t,e){this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),n.Sprite.call(this,n.Texture.fromCanvas(this.canvas)),this.setText(t),this.setStyle(e),this.updateText(),this.dirty=!1},n.Text.prototype=Object.create(n.Sprite.prototype),n.Text.prototype.constructor=n.Text,n.Text.prototype.setStyle=function(t){t=t||{},t.font=t.font||"bold 20pt Arial",t.fill=t.fill||"black",t.align=t.align||"left",t.stroke=t.stroke||"black",t.strokeThickness=t.strokeThickness||0,t.wordWrap=t.wordWrap||!1,t.wordWrapWidth=t.wordWrapWidth||100,this.style=t,this.dirty=!0},n.Sprite.prototype.setText=function(t){this.text=""+t||" ",this.dirty=!0},n.Text.prototype.updateText=function(){this.context.font=this.style.font;var t=this.text;this.style.wordWrap&&(t=this.wordWrap(this.text));for(var e=t.split(/(?:\r\n|\r|\n)/),i=[],r=0,s=0;e.length>s;s++){var o=this.context.measureText(e[s]).width;i[s]=o,r=Math.max(r,o)}this.canvas.width=r+this.style.strokeThickness;var a=this.determineFontHeight("font: "+this.style.font+";")+this.style.strokeThickness;for(this.canvas.height=a*e.length,this.context.fillStyle=this.style.fill,this.context.font=this.style.font,this.context.strokeStyle=this.style.stroke,this.context.lineWidth=this.style.strokeThickness,this.context.textBaseline="top",s=0;e.length>s;s++){var h=new n.Point(this.style.strokeThickness/2,this.style.strokeThickness/2+s*a);"right"==this.style.align?h.x+=r-i[s]:"center"==this.style.align&&(h.x+=(r-i[s])/2),this.style.stroke&&this.style.strokeThickness&&this.context.strokeText(e[s],h.x,h.y),this.style.fill&&this.context.fillText(e[s],h.x,h.y)}this.updateTexture()},n.Text.prototype.updateTexture=function(){this.texture.baseTexture.width=this.canvas.width,this.texture.baseTexture.height=this.canvas.height,this.texture.frame.width=this.canvas.width,this.texture.frame.height=this.canvas.height,this._width=this.canvas.width,this._height=this.canvas.height,n.texturesToUpdate.push(this.texture.baseTexture)},n.Text.prototype.updateTransform=function(){this.dirty&&(this.updateText(),this.dirty=!1),n.Sprite.prototype.updateTransform.call(this)},n.Text.prototype.determineFontHeight=function(t){var e=n.Text.heightCache[t];if(!e){var i=document.getElementsByTagName("body")[0],r=document.createElement("div"),s=document.createTextNode("M");r.appendChild(s),r.setAttribute("style",t+";position:absolute;top:0;left:0"),i.appendChild(r),e=r.offsetHeight,n.Text.heightCache[t]=e,i.removeChild(r)}return e},n.Text.prototype.wordWrap=function(t){for(var e=function(t,e,i,r,n){var s=Math.floor((r-i)/2)+i;return s==i?1:n>=t.measureText(e.substring(0,s)).width?t.measureText(e.substring(0,s+1)).width>n?s:arguments.callee(t,e,s,r,n):arguments.callee(t,e,i,s,n)},i=function(t,i,r){if(r>=t.measureText(i).width||1>i.length)return i;var n=e(t,i,0,i.length,r);return i.substring(0,n)+"\n"+arguments.callee(t,i.substring(n),r)},r="",n=t.split("\n"),s=0;n.length>s;s++)r+=i(this.context,n[s],this.style.wordWrapWidth)+"\n";return r},n.Text.prototype.destroy=function(t){t&&this.texture.destroy()},n.Text.heightCache={},n.BitmapText=function(t,e){n.DisplayObjectContainer.call(this),this.setText(t),this.setStyle(e),this.updateText(),this.dirty=!1},n.BitmapText.prototype=Object.create(n.DisplayObjectContainer.prototype),n.BitmapText.prototype.constructor=n.BitmapText,n.BitmapText.prototype.setText=function(t){this.text=t||" ",this.dirty=!0},n.BitmapText.prototype.setStyle=function(t){t=t||{},t.align=t.align||"left",this.style=t;var e=t.font.split(" ");this.fontName=e[e.length-1],this.fontSize=e.length>=2?parseInt(e[e.length-2],10):n.BitmapText.fonts[this.fontName].size,this.dirty=!0},n.BitmapText.prototype.updateText=function(){for(var t=n.BitmapText.fonts[this.fontName],e=new n.Point,i=null,r=[],s=0,o=[],a=0,h=this.fontSize/t.size,l=0;this.text.length>l;l++){var u=this.text.charCodeAt(l);if(/(?:\r\n|\r|\n)/.test(this.text.charAt(l)))o.push(e.x),s=Math.max(s,e.x),a++,e.x=0,e.y+=t.lineHeight,i=null;else{var c=t.chars[u];c&&(i&&c[i]&&(e.x+=c.kerning[i]),r.push({texture:c.texture,line:a,charCode:u,position:new n.Point(e.x+c.xOffset,e.y+c.yOffset)}),e.x+=c.xAdvance,i=u)}}o.push(e.x),s=Math.max(s,e.x);var p=[];for(l=0;a>=l;l++){var d=0;"right"==this.style.align?d=s-o[l]:"center"==this.style.align&&(d=(s-o[l])/2),p.push(d)}for(l=0;r.length>l;l++){var f=new n.Sprite(r[l].texture);f.position.x=(r[l].position.x+p[r[l].line])*h,f.position.y=r[l].position.y*h,f.scale.x=f.scale.y=h,this.addChild(f)}this.width=e.x*h,this.height=(e.y+t.lineHeight)*h},n.BitmapText.prototype.updateTransform=function(){if(this.dirty){for(;this.children.length>0;)this.removeChild(this.getChildAt(0));this.updateText(),this.dirty=!1}n.DisplayObjectContainer.prototype.updateTransform.call(this)},n.BitmapText.fonts={},n.InteractionManager=function(t){this.stage=t,this.mouse=new n.InteractionData,this.touchs={},this.tempPoint=new n.Point,this.mouseoverEnabled=!0,this.pool=[],this.interactiveItems=[],this.last=0},n.InteractionManager.prototype.constructor=n.InteractionManager,n.InteractionManager.prototype.collectInteractiveSprite=function(t,e){for(var i=t.children,r=i.length,n=r-1;n>=0;n--){var s=i[n];s.visible&&(s.interactive?(e.interactiveChildren=!0,this.interactiveItems.push(s),s.children.length>0&&this.collectInteractiveSprite(s,s)):(s.__iParent=null,s.children.length>0&&this.collectInteractiveSprite(s,e)))}},n.InteractionManager.prototype.setTarget=function(t){window.navigator.msPointerEnabled&&(t.view.style["-ms-content-zooming"]="none",t.view.style["-ms-touch-action"]="none"),this.target=t,t.view.addEventListener("mousemove",this.onMouseMove.bind(this),!0),t.view.addEventListener("mousedown",this.onMouseDown.bind(this),!0),document.body.addEventListener("mouseup",this.onMouseUp.bind(this),!0),t.view.addEventListener("mouseout",this.onMouseUp.bind(this),!0),t.view.addEventListener("touchstart",this.onTouchStart.bind(this),!0),t.view.addEventListener("touchend",this.onTouchEnd.bind(this),!0),t.view.addEventListener("touchmove",this.onTouchMove.bind(this),!0)},n.InteractionManager.prototype.update=function(){if(this.target){var t=Date.now(),e=t-this.last;if(e=30*e/1e3,!(1>e)){if(this.last=t,this.dirty){this.dirty=!1;for(var i=this.interactiveItems.length,r=0;i>r;r++)this.interactiveItems[r].interactiveChildren=!1;this.interactiveItems=[],this.stage.interactive&&this.interactiveItems.push(this.stage),this.collectInteractiveSprite(this.stage,this.stage)}var n=this.interactiveItems.length;this.target.view.style.cursor="default";for(var r=0;n>r;r++){var s=this.interactiveItems[r];s.visible&&(s.mouseover||s.mouseout||s.buttonMode)&&(s.__hit=this.hitTest(s,this.mouse),this.mouse.target=s,s.__hit?(s.buttonMode&&(this.target.view.style.cursor="pointer"),s.__isOver||(s.mouseover&&s.mouseover(this.mouse),s.__isOver=!0)):s.__isOver&&(s.mouseout&&s.mouseout(this.mouse),s.__isOver=!1))}}}},n.InteractionManager.prototype.onMouseMove=function(t){this.mouse.originalEvent=t||window.event;var e=this.target.view.getBoundingClientRect();this.mouse.global.x=(t.clientX-e.left)*(this.target.width/e.width),this.mouse.global.y=(t.clientY-e.top)*(this.target.height/e.height);var i=this.interactiveItems.length;this.mouse.global;for(var r=0;i>r;r++){var n=this.interactiveItems[r];n.mousemove&&n.mousemove(this.mouse)}},n.InteractionManager.prototype.onMouseDown=function(t){t.preventDefault(),this.mouse.originalEvent=t||window.event;var e=this.interactiveItems.length;this.mouse.global,this.stage;for(var i=0;e>i;i++){var r=this.interactiveItems[i];if((r.mousedown||r.click)&&(r.__mouseIsDown=!0,r.__hit=this.hitTest(r,this.mouse),r.__hit&&(r.mousedown&&r.mousedown(this.mouse),r.__isDown=!0,!r.interactiveChildren)))break}},n.InteractionManager.prototype.onMouseUp=function(t){this.mouse.originalEvent=t||window.event,this.mouse.global;for(var e=this.interactiveItems.length,i=!1,r=0;e>r;r++){var n=this.interactiveItems[r];(n.mouseup||n.mouseupoutside||n.click)&&(n.__hit=this.hitTest(n,this.mouse),n.__hit&&!i?(n.mouseup&&n.mouseup(this.mouse),n.__isDown&&n.click&&n.click(this.mouse),n.interactiveChildren||(i=!0)):n.__isDown&&n.mouseupoutside&&n.mouseupoutside(this.mouse),n.__isDown=!1)}},n.InteractionManager.prototype.hitTest=function(t,e){var i=e.global;if(!t.visible)return!1;var r=t instanceof n.Sprite,s=t.worldTransform,o=s[0],a=s[1],h=s[2],l=s[3],u=s[4],c=s[5],p=1/(o*u+a*-l),d=u*p*i.x+-a*p*i.y+(c*a-h*u)*p,f=o*p*i.y+-l*p*i.x+(-c*o+h*l)*p;if(e.target=t,t.hitArea&&t.hitArea.contains)return t.hitArea.contains(d,f)?(e.target=t,!0):!1;if(r){var v,m=t.texture.frame.width,g=t.texture.frame.height,x=-m*t.anchor.x;if(d>x&&x+m>d&&(v=-g*t.anchor.y,f>v&&v+g>f))return e.target=t,!0}for(var b=t.children.length,y=0;b>y;y++){var T=t.children[y],_=this.hitTest(T,e);if(_)return e.target=t,!0}return!1},n.InteractionManager.prototype.onTouchMove=function(t){this.mouse.originalEvent=t||window.event;for(var e=this.target.view.getBoundingClientRect(),i=t.changedTouches,r=0;i.length>r;r++){var n=i[r],s=this.touchs[n.identifier];s.global.x=(n.clientX-e.left)*(this.target.width/e.width),s.global.y=(n.clientY-e.top)*(this.target.height/e.height)}for(var o=this.interactiveItems.length,r=0;o>r;r++){var a=this.interactiveItems[r];a.touchmove&&a.touchmove(s)}},n.InteractionManager.prototype.onTouchStart=function(t){t.preventDefault(),this.mouse.originalEvent=t||window.event;for(var e=this.target.view.getBoundingClientRect(),i=t.changedTouches,r=0;i.length>r;r++){var s=i[r],o=this.pool.pop();o||(o=new n.InteractionData),this.touchs[s.identifier]=o,o.global.x=(s.clientX-e.left)*(this.target.width/e.width),o.global.y=(s.clientY-e.top)*(this.target.height/e.height);for(var a=this.interactiveItems.length,h=0;a>h;h++){var l=this.interactiveItems[h];if((l.touchstart||l.tap)&&(l.__hit=this.hitTest(l,o),l.__hit&&(l.touchstart&&l.touchstart(o),l.__isDown=!0,l.__touchData=o,!l.interactiveChildren)))break}}},n.InteractionManager.prototype.onTouchEnd=function(t){this.mouse.originalEvent=t||window.event;for(var e=this.target.view.getBoundingClientRect(),i=t.changedTouches,r=0;i.length>r;r++){var n=i[r],s=this.touchs[n.identifier],o=!1;s.global.x=(n.clientX-e.left)*(this.target.width/e.width),s.global.y=(n.clientY-e.top)*(this.target.height/e.height);for(var a=this.interactiveItems.length,h=0;a>h;h++){var l=this.interactiveItems[h],u=l.__touchData;l.__hit=this.hitTest(l,s),u==s&&((l.touchend||l.tap)&&(l.__hit&&!o?(l.touchend&&l.touchend(s),l.__isDown&&l.tap&&l.tap(s),l.interactiveChildren||(o=!0)):l.__isDown&&l.touchendoutside&&l.touchendoutside(s),l.__isDown=!1),l.__touchData=null)}this.pool.push(s),this.touchs[n.identifier]=null}},n.InteractionData=function(){this.global=new n.Point,this.local=new n.Point,this.target,this.originalEvent},n.InteractionData.prototype.getLocalPosition=function(t){var e=t.worldTransform,i=this.global,r=e[0],s=e[1],o=e[2],a=e[3],h=e[4],l=e[5],u=1/(r*h+s*-a);return new n.Point(h*u*i.x+-s*u*i.y+(l*s-o*h)*u,r*u*i.y+-a*u*i.x+(-l*r+o*a)*u)},n.InteractionData.prototype.constructor=n.InteractionData,n.Stage=function(t,e){n.DisplayObjectContainer.call(this),this.worldTransform=n.mat3.create(),this.interactive=e,this.interactionManager=new n.InteractionManager(this),this.dirty=!0,this.__childrenAdded=[],this.__childrenRemoved=[],this.stage=this,this.stage.hitArea=new n.Rectangle(0,0,1e5,1e5),this.setBackgroundColor(t),this.worldVisible=!0},n.Stage.prototype=Object.create(n.DisplayObjectContainer.prototype),n.Stage.prototype.constructor=n.Stage,n.Stage.prototype.updateTransform=function(){this.worldAlpha=1;for(var t=0,e=this.children.length;e>t;t++)this.children[t].updateTransform();this.dirty&&(this.dirty=!1,this.interactionManager.dirty=!0),this.interactive&&this.interactionManager.update()},n.Stage.prototype.setBackgroundColor=function(t){this.backgroundColor=t||0,this.backgroundColorSplit=e(this.backgroundColor);var i=this.backgroundColor.toString(16);i="000000".substr(0,6-i.length)+i,this.backgroundColorString="#"+i},n.Stage.prototype.getMousePosition=function(){return this.interactionManager.mouse.global};for(var s=0,o=["ms","moz","webkit","o"],h=0;o.length>h&&!window.requestAnimationFrame;++h)window.requestAnimationFrame=window[o[h]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[o[h]+"CancelAnimationFrame"]||window[o[h]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(t){var e=(new Date).getTime(),i=Math.max(0,16-(e-s)),r=window.setTimeout(function(){t(e+i)},i);return s=e+i,r}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(t){clearTimeout(t)}),window.requestAnimFrame=window.requestAnimationFrame,"function"!=typeof Function.prototype.bind&&(Function.prototype.bind=function(){var t=Array.prototype.slice;return function(e){function i(){var s=n.concat(t.call(arguments));r.apply(this instanceof i?this:e,s)}var r=this,n=t.call(arguments,1);if("function"!=typeof r)throw new TypeError;return i.prototype=function s(t){return t&&(s.prototype=t),this instanceof s?void 0:new s}(r.prototype),i}}());var l=n.AjaxRequest=function(){var t=["Msxml2.XMLHTTP","Microsoft.XMLHTTP"];if(!window.ActiveXObject)return window.XMLHttpRequest?new XMLHttpRequest:!1;for(var e=0;t.length>e;e++)try{return new ActiveXObject(t[e])}catch(i){}};n.runList=function(t){console.log(">>>>>>>>>"),console.log("_");var e=0,i=t.first;for(console.log(i);i._iNext;)if(e++,i=i._iNext,console.log(i),e>100){console.log("BREAK");break}},n.EventTarget=function(){var t={};this.addEventListener=this.on=function(e,i){void 0===t[e]&&(t[e]=[]),-1===t[e].indexOf(i)&&t[e].push(i)},this.dispatchEvent=this.emit=function(e){for(var i in t[e.type])t[e.type][i](e)},this.removeEventListener=this.off=function(e,i){var r=t[e].indexOf(i);-1!==r&&t[e].splice(r,1)}},n.autoDetectRenderer=function(t,e,i,r){t||(t=800),e||(e=600);var s=function(){try{return!!window.WebGLRenderingContext&&!!document.createElement("canvas").getContext("experimental-webgl")}catch(t){return!1}}();return s?new n.WebGLRenderer(t,e,i,r):new n.CanvasRenderer(t,e,i,r)},n.PolyK={},n.PolyK.Triangulate=function(t){var e=!0,i=t.length>>1;if(3>i)return[];for(var r=[],s=[],o=0;i>o;o++)s.push(o);for(var o=0,a=i;a>3;){var h=s[(o+0)%a],l=s[(o+1)%a],u=s[(o+2)%a],c=t[2*h],p=t[2*h+1],d=t[2*l],f=t[2*l+1],v=t[2*u],m=t[2*u+1],g=!1;if(n.PolyK._convex(c,p,d,f,v,m,e)){g=!0;for(var x=0;a>x;x++){var b=s[x];if(b!=h&&b!=l&&b!=u&&n.PolyK._PointInTriangle(t[2*b],t[2*b+1],c,p,d,f,v,m)){g=!1;break}}}if(g)r.push(h,l,u),s.splice((o+1)%a,1),a--,o=0;else if(o++>3*a){if(!e)return console.log("PIXI Warning: shape too complex to fill"),[];var r=[];s=[];for(var o=0;i>o;o++)s.push(o);o=0,a=i,e=!1}}return r.push(s[0],s[1],s[2]),r},n.PolyK._PointInTriangle=function(t,e,i,r,n,s,o,a){var h=o-i,l=a-r,u=n-i,c=s-r,p=t-i,d=e-r,f=h*h+l*l,v=h*u+l*c,m=h*p+l*d,g=u*u+c*c,x=u*p+c*d,b=1/(f*g-v*v),y=(g*m-v*x)*b,T=(f*x-v*m)*b;return y>=0&&T>=0&&1>y+T},n.PolyK._convex=function(t,e,i,r,n,s,o){return(e-r)*(n-i)+(i-t)*(s-r)>=0==o},n.shaderFragmentSrc=["precision mediump float;","varying vec2 vTextureCoord;","varying float vColor;","uniform sampler2D uSampler;","void main(void) {","gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));","gl_FragColor = gl_FragColor * vColor;","}"],n.shaderVertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aTextureCoord;","attribute float aColor;","uniform vec2 projectionVector;","varying vec2 vTextureCoord;","varying float vColor;","void main(void) {","gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);","vTextureCoord = aTextureCoord;","vColor = aColor;","}"],n.stripShaderFragmentSrc=["precision mediump float;","varying vec2 vTextureCoord;","varying float vColor;","uniform float alpha;","uniform sampler2D uSampler;","void main(void) {","gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));","gl_FragColor = gl_FragColor * alpha;","}"],n.stripShaderVertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aTextureCoord;","attribute float aColor;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","varying vec2 vTextureCoord;","varying float vColor;","void main(void) {","vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);","gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);","vTextureCoord = aTextureCoord;","vColor = aColor;","}"],n.primitiveShaderFragmentSrc=["precision mediump float;","varying vec4 vColor;","void main(void) {","gl_FragColor = vColor;","}"],n.primitiveShaderVertexSrc=["attribute vec2 aVertexPosition;","attribute vec4 aColor;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","uniform float alpha;","varying vec4 vColor;","void main(void) {","vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);","gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);","vColor = aColor * alpha;","}"],n.initPrimitiveShader=function(){var t=n.gl,e=n.compileProgram(n.primitiveShaderVertexSrc,n.primitiveShaderFragmentSrc);t.useProgram(e),e.vertexPositionAttribute=t.getAttribLocation(e,"aVertexPosition"),e.colorAttribute=t.getAttribLocation(e,"aColor"),e.projectionVector=t.getUniformLocation(e,"projectionVector"),e.translationMatrix=t.getUniformLocation(e,"translationMatrix"),e.alpha=t.getUniformLocation(e,"alpha"),n.primitiveProgram=e},n.initDefaultShader=function(){var t=this.gl,e=n.compileProgram(n.shaderVertexSrc,n.shaderFragmentSrc);t.useProgram(e),e.vertexPositionAttribute=t.getAttribLocation(e,"aVertexPosition"),e.projectionVector=t.getUniformLocation(e,"projectionVector"),e.textureCoordAttribute=t.getAttribLocation(e,"aTextureCoord"),e.colorAttribute=t.getAttribLocation(e,"aColor"),e.samplerUniform=t.getUniformLocation(e,"uSampler"),n.shaderProgram=e},n.initDefaultStripShader=function(){var t=this.gl,e=n.compileProgram(n.stripShaderVertexSrc,n.stripShaderFragmentSrc);t.useProgram(e),e.vertexPositionAttribute=t.getAttribLocation(e,"aVertexPosition"),e.projectionVector=t.getUniformLocation(e,"projectionVector"),e.textureCoordAttribute=t.getAttribLocation(e,"aTextureCoord"),e.translationMatrix=t.getUniformLocation(e,"translationMatrix"),e.alpha=t.getUniformLocation(e,"alpha"),e.colorAttribute=t.getAttribLocation(e,"aColor"),e.projectionVector=t.getUniformLocation(e,"projectionVector"),e.samplerUniform=t.getUniformLocation(e,"uSampler"),n.stripShaderProgram=e},n.CompileVertexShader=function(t,e){return n._CompileShader(t,e,t.VERTEX_SHADER)},n.CompileFragmentShader=function(t,e){return n._CompileShader(t,e,t.FRAGMENT_SHADER)},n._CompileShader=function(t,e,i){var r=e.join("\n"),n=t.createShader(i);return t.shaderSource(n,r),t.compileShader(n),t.getShaderParameter(n,t.COMPILE_STATUS)?n:(alert(t.getShaderInfoLog(n)),null)},n.compileProgram=function(t,e){var i=n.gl,r=n.CompileFragmentShader(i,e),s=n.CompileVertexShader(i,t),o=i.createProgram();return i.attachShader(o,s),i.attachShader(o,r),i.linkProgram(o),i.getProgramParameter(o,i.LINK_STATUS)||alert("Could not initialise shaders"),o},n.activateDefaultShader=function(){var t=n.gl,e=n.shaderProgram;t.useProgram(e),t.enableVertexAttribArray(e.vertexPositionAttribute),t.enableVertexAttribArray(e.textureCoordAttribute),t.enableVertexAttribArray(e.colorAttribute)},n.activatePrimitiveShader=function(){var t=n.gl;t.disableVertexAttribArray(n.shaderProgram.textureCoordAttribute),t.disableVertexAttribArray(n.shaderProgram.colorAttribute),t.useProgram(n.primitiveProgram),t.enableVertexAttribArray(n.primitiveProgram.vertexPositionAttribute),t.enableVertexAttribArray(n.primitiveProgram.colorAttribute) -},n.WebGLGraphics=function(){},n.WebGLGraphics.renderGraphics=function(t,e){var i=n.gl;t._webGL||(t._webGL={points:[],indices:[],lastIndex:0,buffer:i.createBuffer(),indexBuffer:i.createBuffer()}),t.dirty&&(t.dirty=!1,t.clearDirty&&(t.clearDirty=!1,t._webGL.lastIndex=0,t._webGL.points=[],t._webGL.indices=[]),n.WebGLGraphics.updateGraphics(t)),n.activatePrimitiveShader();var r=n.mat3.clone(t.worldTransform);n.mat3.transpose(r),i.blendFunc(i.ONE,i.ONE_MINUS_SRC_ALPHA),i.uniformMatrix3fv(n.primitiveProgram.translationMatrix,!1,r),i.uniform2f(n.primitiveProgram.projectionVector,e.x,e.y),i.uniform1f(n.primitiveProgram.alpha,t.worldAlpha),i.bindBuffer(i.ARRAY_BUFFER,t._webGL.buffer),i.vertexAttribPointer(n.shaderProgram.vertexPositionAttribute,2,i.FLOAT,!1,0,0),i.vertexAttribPointer(n.primitiveProgram.vertexPositionAttribute,2,i.FLOAT,!1,24,0),i.vertexAttribPointer(n.primitiveProgram.colorAttribute,4,i.FLOAT,!1,24,8),i.bindBuffer(i.ELEMENT_ARRAY_BUFFER,t._webGL.indexBuffer),i.drawElements(i.TRIANGLE_STRIP,t._webGL.indices.length,i.UNSIGNED_SHORT,0),n.activateDefaultShader()},n.WebGLGraphics.updateGraphics=function(t){for(var e=t._webGL.lastIndex;t.graphicsData.length>e;e++){var i=t.graphicsData[e];i.type==n.Graphics.POLY?(i.fill&&i.points.length>3&&n.WebGLGraphics.buildPoly(i,t._webGL),i.lineWidth>0&&n.WebGLGraphics.buildLine(i,t._webGL)):i.type==n.Graphics.RECT?n.WebGLGraphics.buildRectangle(i,t._webGL):(i.type==n.Graphics.CIRC||i.type==n.Graphics.ELIP)&&n.WebGLGraphics.buildCircle(i,t._webGL)}t._webGL.lastIndex=t.graphicsData.length;var r=n.gl;t._webGL.glPoints=new Float32Array(t._webGL.points),r.bindBuffer(r.ARRAY_BUFFER,t._webGL.buffer),r.bufferData(r.ARRAY_BUFFER,t._webGL.glPoints,r.STATIC_DRAW),t._webGL.glIndicies=new Uint16Array(t._webGL.indices),r.bindBuffer(r.ELEMENT_ARRAY_BUFFER,t._webGL.indexBuffer),r.bufferData(r.ELEMENT_ARRAY_BUFFER,t._webGL.glIndicies,r.STATIC_DRAW)},n.WebGLGraphics.buildRectangle=function(t,i){var r=t.points,s=r[0],o=r[1],a=r[2],h=r[3];if(t.fill){var l=e(t.fillColor),u=t.fillAlpha,c=l[0]*u,p=l[1]*u,d=l[2]*u,f=i.points,v=i.indices,m=f.length/6;f.push(s,o),f.push(c,p,d,u),f.push(s+a,o),f.push(c,p,d,u),f.push(s,o+h),f.push(c,p,d,u),f.push(s+a,o+h),f.push(c,p,d,u),v.push(m,m,m+1,m+2,m+3,m+3)}t.lineWidth&&(t.points=[s,o,s+a,o,s+a,o+h,s,o+h,s,o],n.WebGLGraphics.buildLine(t,i))},n.WebGLGraphics.buildCircle=function(t,i){var r=t.points,s=r[0],o=r[1],a=r[2],h=r[3],l=40,u=2*Math.PI/l;if(t.fill){var c=e(t.fillColor),p=t.fillAlpha,d=c[0]*p,f=c[1]*p,v=c[2]*p,m=i.points,g=i.indices,x=m.length/6;g.push(x);for(var b=0;l+1>b;b++)m.push(s,o,d,f,v,p),m.push(s+Math.sin(u*b)*a,o+Math.cos(u*b)*h,d,f,v,p),g.push(x++,x++);g.push(x-1)}if(t.lineWidth){t.points=[];for(var b=0;l+1>b;b++)t.points.push(s+Math.sin(u*b)*a,o+Math.cos(u*b)*h);n.WebGLGraphics.buildLine(t,i)}},n.WebGLGraphics.buildLine=function(t,i){var r=t.points;if(0!=r.length){var s=new n.Point(r[0],r[1]),o=new n.Point(r[r.length-2],r[r.length-1]);if(s.x==o.x&&s.y==o.y){r.pop(),r.pop(),o=new n.Point(r[r.length-2],r[r.length-1]);var a=o.x+.5*(s.x-o.x),h=o.y+.5*(s.y-o.y);r.unshift(a,h),r.push(a,h)}var l,u,c,p,d,f,v,m,g,x,b,y,T,_,w,A,R,S,C,L,E,P=i.points,B=i.indices,F=r.length/2,G=r.length,I=P.length/6,D=t.lineWidth/2,M=e(t.lineColor),O=t.lineAlpha,W=M[0]*O,U=M[1]*O,k=M[2]*O;l=r[0],u=r[1],c=r[2],p=r[3],v=-(u-p),m=l-c,E=Math.sqrt(v*v+m*m),v/=E,m/=E,v*=D,m*=D,P.push(l-v,u-m,W,U,k,O),P.push(l+v,u+m,W,U,k,O);for(var j=1;F-1>j;j++)l=r[2*(j-1)],u=r[2*(j-1)+1],c=r[2*j],p=r[2*j+1],d=r[2*(j+1)],f=r[2*(j+1)+1],v=-(u-p),m=l-c,E=Math.sqrt(v*v+m*m),v/=E,m/=E,v*=D,m*=D,g=-(p-f),x=c-d,E=Math.sqrt(g*g+x*x),g/=E,x/=E,g*=D,x*=D,T=-m+u-(-m+p),_=-v+c-(-v+l),w=(-v+l)*(-m+p)-(-v+c)*(-m+u),A=-x+f-(-x+p),R=-g+c-(-g+d),S=(-g+d)*(-x+p)-(-g+c)*(-x+f),C=T*R-A*_,0==C&&(C+=1),px=(_*S-R*w)/C,py=(A*w-T*S)/C,L=(px-c)*(px-c)+(py-p)+(py-p),L>19600?(b=v-g,y=m-x,E=Math.sqrt(b*b+y*y),b/=E,y/=E,b*=D,y*=D,P.push(c-b,p-y),P.push(W,U,k,O),P.push(c+b,p+y),P.push(W,U,k,O),P.push(c-b,p-y),P.push(W,U,k,O),G++):(P.push(px,py),P.push(W,U,k,O),P.push(c-(px-c),p-(py-p)),P.push(W,U,k,O));l=r[2*(F-2)],u=r[2*(F-2)+1],c=r[2*(F-1)],p=r[2*(F-1)+1],v=-(u-p),m=l-c,E=Math.sqrt(v*v+m*m),v/=E,m/=E,v*=D,m*=D,P.push(c-v,p-m),P.push(W,U,k,O),P.push(c+v,p+m),P.push(W,U,k,O),B.push(I);for(var j=0;G>j;j++)B.push(I++);B.push(I-1)}},n.WebGLGraphics.buildPoly=function(t,i){var r=t.points;if(!(6>r.length)){for(var s=i.points,o=i.indices,a=r.length/2,h=e(t.fillColor),l=t.fillAlpha,u=h[0]*l,c=h[1]*l,p=h[2]*l,d=n.PolyK.Triangulate(r),f=s.length/6,v=0;d.length>v;v+=3)o.push(d[v]+f),o.push(d[v]+f),o.push(d[v+1]+f),o.push(d[v+2]+f),o.push(d[v+2]+f);for(var v=0;a>v;v++)s.push(r[2*v],r[2*v+1],u,c,p,l)}},n._defaultFrame=new n.Rectangle(0,0,1,1),n.gl,n.WebGLRenderer=function(t,e,i,r){this.transparent=!!r,this.width=t||800,this.height=e||600,this.view=i||document.createElement("canvas"),this.view.width=this.width,this.view.height=this.height;var s=this;this.view.addEventListener("webglcontextlost",function(t){s.handleContextLost(t)},!1),this.view.addEventListener("webglcontextrestored",function(t){s.handleContextRestored(t)},!1),this.batchs=[];try{n.gl=this.gl=this.view.getContext("experimental-webgl",{alpha:this.transparent,antialias:!0,premultipliedAlpha:!1,stencil:!0})}catch(o){throw Error(" This browser does not support webGL. Try using the canvas renderer"+this)}n.initPrimitiveShader(),n.initDefaultShader(),n.initDefaultStripShader(),n.activateDefaultShader();var a=this.gl;n.WebGLRenderer.gl=a,this.batch=new n.WebGLBatch(a),a.disable(a.DEPTH_TEST),a.disable(a.CULL_FACE),a.enable(a.BLEND),a.colorMask(!0,!0,!0,this.transparent),n.projection=new n.Point(400,300),this.resize(this.width,this.height),this.contextLost=!1,this.stageRenderGroup=new n.WebGLRenderGroup(this.gl)},n.WebGLRenderer.prototype.constructor=n.WebGLRenderer,n.WebGLRenderer.getBatch=function(){return 0==n._batchs.length?new n.WebGLBatch(n.WebGLRenderer.gl):n._batchs.pop()},n.WebGLRenderer.returnBatch=function(t){t.clean(),n._batchs.push(t)},n.WebGLRenderer.prototype.render=function(t){if(!this.contextLost){this.__stage!==t&&(this.__stage=t,this.stageRenderGroup.setRenderable(t)),n.WebGLRenderer.updateTextures(),t.updateTransform();var e=this.gl;if(e.colorMask(!0,!0,!0,this.transparent),e.viewport(0,0,this.width,this.height),e.bindFramebuffer(e.FRAMEBUFFER,null),e.clearColor(t.backgroundColorSplit[0],t.backgroundColorSplit[1],t.backgroundColorSplit[2],!this.transparent),e.clear(e.COLOR_BUFFER_BIT),this.stageRenderGroup.backgroundColor=t.backgroundColorSplit,this.stageRenderGroup.render(n.projection),t.interactive&&(t._interactiveEventsAdded||(t._interactiveEventsAdded=!0,t.interactionManager.setTarget(this))),n.Texture.frameUpdates.length>0){for(var i=0;n.Texture.frameUpdates.length>i;i++)n.Texture.frameUpdates[i].updateFrame=!1;n.Texture.frameUpdates=[]}}},n.WebGLRenderer.updateTextures=function(){for(var t=0;n.texturesToUpdate.length>t;t++)this.updateTexture(n.texturesToUpdate[t]);for(var t=0;n.texturesToDestroy.length>t;t++)this.destroyTexture(n.texturesToDestroy[t]);n.texturesToUpdate=[],n.texturesToDestroy=[]},n.WebGLRenderer.updateTexture=function(t){var e=n.gl;t._glTexture||(t._glTexture=e.createTexture()),t.hasLoaded&&(e.bindTexture(e.TEXTURE_2D,t._glTexture),e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,t.source),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),t._powerOf2?(e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.REPEAT),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.REPEAT)):(e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE)),e.bindTexture(e.TEXTURE_2D,null))},n.WebGLRenderer.prototype.destroyTexture=function(t){var e=this.gl;t._glTexture&&(t._glTexture=e.createTexture(),e.deleteTexture(e.TEXTURE_2D,t._glTexture))},n.WebGLRenderer.prototype.resize=function(t,e){this.width=t,this.height=e,this.view.width=t,this.view.height=e,this.gl.viewport(0,0,this.width,this.height),n.projection.x=this.width/2,n.projection.y=this.height/2},n.WebGLRenderer.prototype.handleContextLost=function(t){t.preventDefault(),this.contextLost=!0},n.WebGLRenderer.prototype.handleContextRestored=function(){this.gl=this.view.getContext("experimental-webgl",{alpha:!0}),this.initShaders();for(var t in n.TextureCache){var e=n.TextureCache[t].baseTexture;e._glTexture=null,n.WebGLRenderer.updateTexture(e)}for(var i=0;this.batchs.length>i;i++)this.batchs[i].restoreLostContext(this.gl),this.batchs[i].dirty=!0;n._restoreBatchs(this.gl),this.contextLost=!1},n._batchs=[],n._getBatch=function(t){return 0==n._batchs.length?new n.WebGLBatch(t):n._batchs.pop()},n._returnBatch=function(t){t.clean(),n._batchs.push(t)},n._restoreBatchs=function(t){for(var e=0;n._batchs.length>e;e++)n._batchs[e].restoreLostContext(t)},n.WebGLBatch=function(t){this.gl=t,this.size=0,this.vertexBuffer=t.createBuffer(),this.indexBuffer=t.createBuffer(),this.uvBuffer=t.createBuffer(),this.colorBuffer=t.createBuffer(),this.blendMode=n.blendModes.NORMAL,this.dynamicSize=1},n.WebGLBatch.prototype.constructor=n.WebGLBatch,n.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,this.tail},n.WebGLBatch.prototype.restoreLostContext=function(t){this.gl=t,this.vertexBuffer=t.createBuffer(),this.indexBuffer=t.createBuffer(),this.uvBuffer=t.createBuffer(),this.colorBuffer=t.createBuffer()},n.WebGLBatch.prototype.init=function(t){t.batch=this,this.dirty=!0,this.blendMode=t.blendMode,this.texture=t.texture.baseTexture,this.head=t,this.tail=t,this.size=1,this.growBatch()},n.WebGLBatch.prototype.insertBefore=function(t,e){this.size++,t.batch=this,this.dirty=!0;var i=e.__prev;e.__prev=t,t.__next=e,i?(t.__prev=i,i.__next=t):this.head=t},n.WebGLBatch.prototype.insertAfter=function(t,e){this.size++,t.batch=this,this.dirty=!0;var i=e.__next;e.__next=t,t.__prev=e,i?(t.__next=i,i.__prev=t):this.tail=t},n.WebGLBatch.prototype.remove=function(t){return this.size--,0==this.size?(t.batch=null,t.__prev=null,t.__next=null,void 0):(t.__prev?t.__prev.__next=t.__next:(this.head=t.__next,this.head.__prev=null),t.__next?t.__next.__prev=t.__prev:(this.tail=t.__prev,this.tail.__next=null),t.batch=null,t.__next=null,t.__prev=null,this.dirty=!0,void 0)},n.WebGLBatch.prototype.split=function(t){this.dirty=!0;var e=new n.WebGLBatch(this.gl);e.init(t),e.texture=this.texture,e.tail=this.tail,this.tail=t.__prev,this.tail.__next=null,t.__prev=null;for(var i=0;t;)i++,t.batch=e,t=t.__next;return e.size=i,this.size-=i,e},n.WebGLBatch.prototype.merge=function(t){this.dirty=!0,this.tail.__next=t.head,t.head.__prev=this.tail,this.size+=t.size,this.tail=t.tail;for(var e=t.head;e;)e.batch=this,e=e.__next},n.WebGLBatch.prototype.growBatch=function(){var t=this.gl;this.dynamicSize=1==this.size?1:1.5*this.size,this.verticies=new Float32Array(8*this.dynamicSize),t.bindBuffer(t.ARRAY_BUFFER,this.vertexBuffer),t.bufferData(t.ARRAY_BUFFER,this.verticies,t.DYNAMIC_DRAW),this.uvs=new Float32Array(8*this.dynamicSize),t.bindBuffer(t.ARRAY_BUFFER,this.uvBuffer),t.bufferData(t.ARRAY_BUFFER,this.uvs,t.DYNAMIC_DRAW),this.dirtyUVS=!0,this.colors=new Float32Array(4*this.dynamicSize),t.bindBuffer(t.ARRAY_BUFFER,this.colorBuffer),t.bufferData(t.ARRAY_BUFFER,this.colors,t.DYNAMIC_DRAW),this.dirtyColors=!0,this.indices=new Uint16Array(6*this.dynamicSize);for(var e=this.indices.length/6,i=0;e>i;i++){var r=6*i,n=4*i;this.indices[r+0]=n+0,this.indices[r+1]=n+1,this.indices[r+2]=n+2,this.indices[r+3]=n+0,this.indices[r+4]=n+2,this.indices[r+5]=n+3}t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,this.indices,t.STATIC_DRAW)},n.WebGLBatch.prototype.refresh=function(){this.gl,this.dynamicSizer;r++)i=this.batchs[r],i instanceof n.WebGLBatch?this.batchs[r].render():i instanceof n.TilingSprite?i.visible&&this.renderTilingSprite(i,t):i instanceof n.Strip?i.visible&&this.renderStrip(i,t):i instanceof n.Graphics?i.visible&&i.renderable&&n.WebGLGraphics.renderGraphics(i,t):i instanceof n.FilterBlock&&(i.open?(e.enable(e.STENCIL_TEST),e.colorMask(!1,!1,!1,!1),e.stencilFunc(e.ALWAYS,1,255),e.stencilOp(e.KEEP,e.KEEP,e.REPLACE),n.WebGLGraphics.renderGraphics(i.mask,t),e.colorMask(!0,!0,!0,!1),e.stencilFunc(e.NOTEQUAL,0,255),e.stencilOp(e.KEEP,e.KEEP,e.KEEP)):e.disable(e.STENCIL_TEST))},n.WebGLRenderGroup.prototype.handleFilter=function(){},n.WebGLRenderGroup.prototype.renderSpecific=function(t,e){n.WebGLRenderer.updateTextures();var i=this.gl;this.checkVisibility(t,t.visible),i.uniform2f(n.shaderProgram.projectionVector,e.x,e.y);for(var r,s,o,a,h=t.first;h._iNext&&(h=h._iNext,!h.renderable||!h.__renderGroup););var l=h.batch;if(h instanceof n.Sprite){l=h.batch;var u=l.head;if(u==h)r=0;else for(r=1;u.__next!=h;)r++,u=u.__next}else l=h;for(var c,p=t,d=t;d.children.length>0;)d=d.children[d.children.length-1],d.renderable&&(p=d);if(p instanceof n.Sprite){c=p.batch;var u=c.head;if(u==p)o=0;else for(o=1;u.__next!=p;)o++,u=u.__next}else c=p;if(l==c)return l instanceof n.WebGLBatch?l.render(r,o+1):this.renderSpecial(l,e),void 0;s=this.batchs.indexOf(l),a=this.batchs.indexOf(c),l instanceof n.WebGLBatch?l.render(r):this.renderSpecial(l,e);for(var f=s+1;a>f;f++)renderable=this.batchs[f],renderable instanceof n.WebGLBatch?this.batchs[f].render():this.renderSpecial(renderable,e);c instanceof n.WebGLBatch?c.render(0,o+1):this.renderSpecial(c,e)},n.WebGLRenderGroup.prototype.renderSpecial=function(t,e){if(t instanceof n.TilingSprite)t.visible&&this.renderTilingSprite(t,e);else if(t instanceof n.Strip)t.visible&&this.renderStrip(t,e);else if(t instanceof n.CustomRenderable)t.visible&&t.renderWebGL(this,e);else if(t instanceof n.Graphics)t.visible&&t.renderable&&n.WebGLGraphics.renderGraphics(t,e);else if(t instanceof n.FilterBlock){var i=n.gl;t.open?(i.enable(i.STENCIL_TEST),i.colorMask(!1,!1,!1,!1),i.stencilFunc(i.ALWAYS,1,255),i.stencilOp(i.KEEP,i.KEEP,i.REPLACE),n.WebGLGraphics.renderGraphics(t.mask,e),i.colorMask(!0,!0,!0,!0),i.stencilFunc(i.NOTEQUAL,0,255),i.stencilOp(i.KEEP,i.KEEP,i.KEEP)):i.disable(i.STENCIL_TEST)}},n.WebGLRenderGroup.prototype.checkVisibility=function(t,e){for(var i=t.children,r=0;i.length>r;r++){var n=i[r];n.worldVisible=n.visible&&e,n.textureChange&&(n.textureChange=!1,n.worldVisible&&this.updateTexture(n)),n.children.length>0&&this.checkVisibility(n,n.worldVisible)}},n.WebGLRenderGroup.prototype.updateTexture=function(t){this.removeObject(t);for(var e=t.first;e!=this.root&&(e=e._iPrev,!e.renderable||!e.__renderGroup););for(var i=t.last;i._iNext&&(i=i._iNext,!i.renderable||!i.__renderGroup););this.insertObject(t,e,i)},n.WebGLRenderGroup.prototype.addFilterBlocks=function(t,e){t.__renderGroup=this,e.__renderGroup=this;for(var i=t;i!=this.root&&(i=i._iPrev,!i.renderable||!i.__renderGroup););this.insertAfter(t,i);for(var r=e;r!=this.root&&(r=r._iPrev,!r.renderable||!r.__renderGroup););this.insertAfter(e,r)},n.WebGLRenderGroup.prototype.removeFilterBlocks=function(t,e){this.removeObject(t),this.removeObject(e)},n.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(t){t.__renderGroup&&t.__renderGroup.removeDisplayObjectAndChildren(t);for(var e=t.first;e!=this.root.first&&(e=e._iPrev,!e.renderable||!e.__renderGroup););for(var i=t.last;i._iNext&&(i=i._iNext,!i.renderable||!i.__renderGroup););var r=t.first,n=t.last._iNext;do r.__renderGroup=this,r.renderable&&(this.insertObject(r,e,i),e=r),r=r._iNext;while(r!=n)},n.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren=function(t){if(t.__renderGroup==this){t.last;do t.__renderGroup=null,t.renderable&&this.removeObject(t),t=t._iNext;while(t)}},n.WebGLRenderGroup.prototype.insertObject=function(t,e,i){var r=e,s=i;if(t instanceof n.Sprite){var o,a;if(r instanceof n.Sprite){if(o=r.batch,o&&o.texture==t.texture.baseTexture&&o.blendMode==t.blendMode)return o.insertAfter(t,r),void 0}else o=r;if(s)if(s instanceof n.Sprite){if(a=s.batch){if(a.texture==t.texture.baseTexture&&a.blendMode==t.blendMode)return a.insertBefore(t,s),void 0;if(a==o){var h=o.split(s),l=n.WebGLRenderer.getBatch(),u=this.batchs.indexOf(o);return l.init(t),this.batchs.splice(u+1,0,l,h),void 0}}}else a=s;var l=n.WebGLRenderer.getBatch();if(l.init(t),o){var u=this.batchs.indexOf(o);this.batchs.splice(u+1,0,l)}else this.batchs.push(l)}else t instanceof n.TilingSprite?this.initTilingSprite(t):t instanceof n.Strip&&this.initStrip(t),this.insertAfter(t,r)},n.WebGLRenderGroup.prototype.insertAfter=function(t,e){if(e instanceof n.Sprite){var i=e.batch;if(i)if(i.tail==e){var r=this.batchs.indexOf(i);this.batchs.splice(r+1,0,t)}else{var s=i.split(e.__next),r=this.batchs.indexOf(i);this.batchs.splice(r+1,0,t,s)}else this.batchs.push(t)}else{var r=this.batchs.indexOf(e);this.batchs.splice(r+1,0,t)}},n.WebGLRenderGroup.prototype.removeObject=function(t){var e;if(t instanceof n.Sprite){var i=t.batch;if(!i)return;i.remove(t),0==i.size&&(e=i)}else e=t;if(e){var r=this.batchs.indexOf(e);if(-1==r)return;if(0==r||r==this.batchs.length-1)return this.batchs.splice(r,1),e instanceof n.WebGLBatch&&n.WebGLRenderer.returnBatch(e),void 0;if(this.batchs[r-1]instanceof n.WebGLBatch&&this.batchs[r+1]instanceof n.WebGLBatch&&this.batchs[r-1].texture==this.batchs[r+1].texture&&this.batchs[r-1].blendMode==this.batchs[r+1].blendMode)return this.batchs[r-1].merge(this.batchs[r+1]),e instanceof n.WebGLBatch&&n.WebGLRenderer.returnBatch(e),n.WebGLRenderer.returnBatch(this.batchs[r+1]),this.batchs.splice(r,2),void 0;this.batchs.splice(r,1),e instanceof n.WebGLBatch&&n.WebGLRenderer.returnBatch(e)}},n.WebGLRenderGroup.prototype.initTilingSprite=function(t){var e=this.gl;t.verticies=new Float32Array([0,0,t.width,0,t.width,t.height,0,t.height]),t.uvs=new Float32Array([0,0,1,0,1,1,0,1]),t.colors=new Float32Array([1,1,1,1]),t.indices=new Uint16Array([0,1,3,2]),t._vertexBuffer=e.createBuffer(),t._indexBuffer=e.createBuffer(),t._uvBuffer=e.createBuffer(),t._colorBuffer=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,t._vertexBuffer),e.bufferData(e.ARRAY_BUFFER,t.verticies,e.STATIC_DRAW),e.bindBuffer(e.ARRAY_BUFFER,t._uvBuffer),e.bufferData(e.ARRAY_BUFFER,t.uvs,e.DYNAMIC_DRAW),e.bindBuffer(e.ARRAY_BUFFER,t._colorBuffer),e.bufferData(e.ARRAY_BUFFER,t.colors,e.STATIC_DRAW),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,t._indexBuffer),e.bufferData(e.ELEMENT_ARRAY_BUFFER,t.indices,e.STATIC_DRAW),t.texture.baseTexture._glTexture?(e.bindTexture(e.TEXTURE_2D,t.texture.baseTexture._glTexture),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.REPEAT),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.REPEAT),t.texture.baseTexture._powerOf2=!0):t.texture.baseTexture._powerOf2=!0},n.WebGLRenderGroup.prototype.renderStrip=function(t,e){var i=this.gl,r=n.shaderProgram;i.useProgram(n.stripShaderProgram);var s=n.mat3.clone(t.worldTransform);n.mat3.transpose(s),i.uniformMatrix3fv(n.stripShaderProgram.translationMatrix,!1,s),i.uniform2f(n.stripShaderProgram.projectionVector,e.x,e.y),i.uniform1f(n.stripShaderProgram.alpha,t.worldAlpha),t.dirty?(t.dirty=!1,i.bindBuffer(i.ARRAY_BUFFER,t._vertexBuffer),i.bufferData(i.ARRAY_BUFFER,t.verticies,i.STATIC_DRAW),i.vertexAttribPointer(r.vertexPositionAttribute,2,i.FLOAT,!1,0,0),i.bindBuffer(i.ARRAY_BUFFER,t._uvBuffer),i.bufferData(i.ARRAY_BUFFER,t.uvs,i.STATIC_DRAW),i.vertexAttribPointer(r.textureCoordAttribute,2,i.FLOAT,!1,0,0),i.activeTexture(i.TEXTURE0),i.bindTexture(i.TEXTURE_2D,t.texture.baseTexture._glTexture),i.bindBuffer(i.ARRAY_BUFFER,t._colorBuffer),i.bufferData(i.ARRAY_BUFFER,t.colors,i.STATIC_DRAW),i.vertexAttribPointer(r.colorAttribute,1,i.FLOAT,!1,0,0),i.bindBuffer(i.ELEMENT_ARRAY_BUFFER,t._indexBuffer),i.bufferData(i.ELEMENT_ARRAY_BUFFER,t.indices,i.STATIC_DRAW)):(i.bindBuffer(i.ARRAY_BUFFER,t._vertexBuffer),i.bufferSubData(i.ARRAY_BUFFER,0,t.verticies),i.vertexAttribPointer(r.vertexPositionAttribute,2,i.FLOAT,!1,0,0),i.bindBuffer(i.ARRAY_BUFFER,t._uvBuffer),i.vertexAttribPointer(r.textureCoordAttribute,2,i.FLOAT,!1,0,0),i.activeTexture(i.TEXTURE0),i.bindTexture(i.TEXTURE_2D,t.texture.baseTexture._glTexture),i.bindBuffer(i.ARRAY_BUFFER,t._colorBuffer),i.vertexAttribPointer(r.colorAttribute,1,i.FLOAT,!1,0,0),i.bindBuffer(i.ELEMENT_ARRAY_BUFFER,t._indexBuffer)),i.drawElements(i.TRIANGLE_STRIP,t.indices.length,i.UNSIGNED_SHORT,0),i.useProgram(n.shaderProgram)},n.WebGLRenderGroup.prototype.renderTilingSprite=function(t,e){var i=this.gl;n.shaderProgram;var r=t.tilePosition,s=t.tileScale,o=r.x/t.texture.baseTexture.width,a=r.y/t.texture.baseTexture.height,h=t.width/t.texture.baseTexture.width/s.x,l=t.height/t.texture.baseTexture.height/s.y;t.uvs[0]=0-o,t.uvs[1]=0-a,t.uvs[2]=1*h-o,t.uvs[3]=0-a,t.uvs[4]=1*h-o,t.uvs[5]=1*l-a,t.uvs[6]=0-o,t.uvs[7]=1*l-a,i.bindBuffer(i.ARRAY_BUFFER,t._uvBuffer),i.bufferSubData(i.ARRAY_BUFFER,0,t.uvs),this.renderStrip(t,e)},n.WebGLRenderGroup.prototype.initStrip=function(t){var e=this.gl;this.shaderProgram,t._vertexBuffer=e.createBuffer(),t._indexBuffer=e.createBuffer(),t._uvBuffer=e.createBuffer(),t._colorBuffer=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,t._vertexBuffer),e.bufferData(e.ARRAY_BUFFER,t.verticies,e.DYNAMIC_DRAW),e.bindBuffer(e.ARRAY_BUFFER,t._uvBuffer),e.bufferData(e.ARRAY_BUFFER,t.uvs,e.STATIC_DRAW),e.bindBuffer(e.ARRAY_BUFFER,t._colorBuffer),e.bufferData(e.ARRAY_BUFFER,t.colors,e.STATIC_DRAW),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,t._indexBuffer),e.bufferData(e.ELEMENT_ARRAY_BUFFER,t.indices,e.STATIC_DRAW)},n.CanvasRenderer=function(t,e,i,r){this.transparent=r,this.width=t||800,this.height=e||600,this.view=i||document.createElement("canvas"),this.context=this.view.getContext("2d"),this.refresh=!0,this.view.width=this.width,this.view.height=this.height,this.count=0},n.CanvasRenderer.prototype.constructor=n.CanvasRenderer,n.CanvasRenderer.prototype.render=function(t){n.texturesToUpdate=[],n.texturesToDestroy=[],t.updateTransform(),this.view.style.backgroundColor==t.backgroundColorString||this.transparent||(this.view.style.backgroundColor=t.backgroundColorString),this.context.setTransform(1,0,0,1,0,0),this.context.clearRect(0,0,this.width,this.height),this.renderDisplayObject(t),t.interactive&&(t._interactiveEventsAdded||(t._interactiveEventsAdded=!0,t.interactionManager.setTarget(this))),n.Texture.frameUpdates.length>0&&(n.Texture.frameUpdates=[])},n.CanvasRenderer.prototype.resize=function(t,e){this.width=t,this.height=e,this.view.width=t,this.view.height=e},n.CanvasRenderer.prototype.renderDisplayObject=function(t){var e,i=this.context;i.globalCompositeOperation="source-over";var r=t.last._iNext;t=t.first;do if(e=t.worldTransform,t.visible)if(t.renderable){if(t instanceof n.Sprite){var s=t.texture.frame;s&&(i.globalAlpha=t.worldAlpha,i.setTransform(e[0],e[3],e[1],e[4],e[2],e[5]),i.drawImage(t.texture.baseTexture.source,s.x,s.y,s.width,s.height,t.anchor.x*-s.width,t.anchor.y*-s.height,s.width,s.height))}else if(t instanceof n.Strip)i.setTransform(e[0],e[3],e[1],e[4],e[2],e[5]),this.renderStrip(t);else if(t instanceof n.TilingSprite)i.setTransform(e[0],e[3],e[1],e[4],e[2],e[5]),this.renderTilingSprite(t);else if(t instanceof n.CustomRenderable)t.renderCanvas(this);else if(t instanceof n.Graphics)i.setTransform(e[0],e[3],e[1],e[4],e[2],e[5]),n.CanvasGraphics.renderGraphics(t,i);else if(t instanceof n.FilterBlock)if(t.open){i.save();var o=t.mask.alpha,a=t.mask.worldTransform;i.setTransform(a[0],a[3],a[1],a[4],a[2],a[5]),t.mask.worldAlpha=.5,i.worldAlpha=0,n.CanvasGraphics.renderGraphicsMask(t.mask,i),i.clip(),t.mask.worldAlpha=o}else i.restore();t=t._iNext}else t=t._iNext;else t=t.last._iNext;while(t!=r)},n.CanvasRenderer.prototype.renderStripFlat=function(t){var e=this.context,i=t.verticies;t.uvs;var r=i.length/2;this.count++,e.beginPath();for(var n=1;r-2>n;n++){var s=2*n,o=i[s],a=i[s+2],h=i[s+4],l=i[s+1],u=i[s+3],c=i[s+5];e.moveTo(o,l),e.lineTo(a,u),e.lineTo(h,c)}e.fillStyle="#FF0000",e.fill(),e.closePath()},n.CanvasRenderer.prototype.renderTilingSprite=function(t){var e=this.context;e.globalAlpha=t.worldAlpha,t.__tilePattern||(t.__tilePattern=e.createPattern(t.texture.baseTexture.source,"repeat")),e.beginPath();var i=t.tilePosition,r=t.tileScale;e.scale(r.x,r.y),e.translate(i.x,i.y),e.fillStyle=t.__tilePattern,e.fillRect(-i.x,-i.y,t.width/r.x,t.height/r.y),e.scale(1/r.x,1/r.y),e.translate(-i.x,-i.y),e.closePath()},n.CanvasRenderer.prototype.renderStrip=function(t){var e=this.context,i=t.verticies,r=t.uvs,n=i.length/2;this.count++;for(var s=1;n-2>s;s++){var o=2*s,a=i[o],h=i[o+2],l=i[o+4],u=i[o+1],c=i[o+3],p=i[o+5],d=r[o]*t.texture.width,f=r[o+2]*t.texture.width,v=r[o+4]*t.texture.width,m=r[o+1]*t.texture.height,g=r[o+3]*t.texture.height,x=r[o+5]*t.texture.height;e.save(),e.beginPath(),e.moveTo(a,u),e.lineTo(h,c),e.lineTo(l,p),e.closePath(),e.clip();var b=d*g+m*v+f*x-g*v-m*f-d*x,y=a*g+m*l+h*x-g*l-m*h-a*x,T=d*h+a*v+f*l-h*v-a*f-d*l,_=d*g*l+m*h*v+a*f*x-a*g*v-m*f*l-d*h*x,w=u*g+m*p+c*x-g*p-m*c-u*x,A=d*c+u*v+f*p-c*v-u*f-d*p,R=d*g*p+m*c*v+u*f*x-u*g*v-m*f*p-d*c*x;e.transform(y/b,w/b,T/b,A/b,_/b,R/b),e.drawImage(t.texture.baseTexture.source,0,0),e.restore()}},n.CanvasGraphics=function(){},n.CanvasGraphics.renderGraphics=function(t,e){for(var i=t.worldAlpha,r=0;t.graphicsData.length>r;r++){var s=t.graphicsData[r],o=s.points;if(e.strokeStyle=color="#"+("00000"+(0|s.lineColor).toString(16)).substr(-6),e.lineWidth=s.lineWidth,s.type==n.Graphics.POLY){e.beginPath(),e.moveTo(o[0],o[1]);for(var a=1;o.length/2>a;a++)e.lineTo(o[2*a],o[2*a+1]);o[0]==o[o.length-2]&&o[1]==o[o.length-1]&&e.closePath(),s.fill&&(e.globalAlpha=s.fillAlpha*i,e.fillStyle=color="#"+("00000"+(0|s.fillColor).toString(16)).substr(-6),e.fill()),s.lineWidth&&(e.globalAlpha=s.lineAlpha*i,e.stroke())}else if(s.type==n.Graphics.RECT)s.fillColor&&(e.globalAlpha=s.fillAlpha*i,e.fillStyle=color="#"+("00000"+(0|s.fillColor).toString(16)).substr(-6),e.fillRect(o[0],o[1],o[2],o[3])),s.lineWidth&&(e.globalAlpha=s.lineAlpha*i,e.strokeRect(o[0],o[1],o[2],o[3]));else if(s.type==n.Graphics.CIRC)e.beginPath(),e.arc(o[0],o[1],o[2],0,2*Math.PI),e.closePath(),s.fill&&(e.globalAlpha=s.fillAlpha*i,e.fillStyle=color="#"+("00000"+(0|s.fillColor).toString(16)).substr(-6),e.fill()),s.lineWidth&&(e.globalAlpha=s.lineAlpha*i,e.stroke());else if(s.type==n.Graphics.ELIP){var h=s.points,l=2*h[2],u=2*h[3],c=h[0]-l/2,p=h[1]-u/2;e.beginPath();var d=.5522848,f=l/2*d,v=u/2*d,m=c+l,g=p+u,x=c+l/2,b=p+u/2;e.moveTo(c,b),e.bezierCurveTo(c,b-v,x-f,p,x,p),e.bezierCurveTo(x+f,p,m,b-v,m,b),e.bezierCurveTo(m,b+v,x+f,g,x,g),e.bezierCurveTo(x-f,g,c,b+v,c,b),e.closePath(),s.fill&&(e.globalAlpha=s.fillAlpha*i,e.fillStyle=color="#"+("00000"+(0|s.fillColor).toString(16)).substr(-6),e.fill()),s.lineWidth&&(e.globalAlpha=s.lineAlpha*i,e.stroke())}}},n.CanvasGraphics.renderGraphicsMask=function(t,e){t.worldAlpha;var i=t.graphicsData.length;i>1&&(i=1,console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object"));for(var r=0;1>r;r++){var s=t.graphicsData[r],o=s.points;if(s.type==n.Graphics.POLY){e.beginPath(),e.moveTo(o[0],o[1]);for(var a=1;o.length/2>a;a++)e.lineTo(o[2*a],o[2*a+1]);o[0]==o[o.length-2]&&o[1]==o[o.length-1]&&e.closePath()}else if(s.type==n.Graphics.RECT)e.beginPath(),e.rect(o[0],o[1],o[2],o[3]),e.closePath();else if(s.type==n.Graphics.CIRC)e.beginPath(),e.arc(o[0],o[1],o[2],0,2*Math.PI),e.closePath();else if(s.type==n.Graphics.ELIP){var h=s.points,l=2*h[2],u=2*h[3],c=h[0]-l/2,p=h[1]-u/2;e.beginPath();var d=.5522848,f=l/2*d,v=u/2*d,m=c+l,g=p+u,x=c+l/2,b=p+u/2;e.moveTo(c,b),e.bezierCurveTo(c,b-v,x-f,p,x,p),e.bezierCurveTo(x+f,p,m,b-v,m,b),e.bezierCurveTo(m,b+v,x+f,g,x,g),e.bezierCurveTo(x-f,g,c,b+v,c,b),e.closePath()}}},n.Graphics=function(){n.DisplayObjectContainer.call(this),this.renderable=!0,this.fillAlpha=1,this.lineWidth=0,this.lineColor="black",this.graphicsData=[],this.currentPath={points:[]}},n.Graphics.prototype=Object.create(n.DisplayObjectContainer.prototype),n.Graphics.prototype.constructor=n.Graphics,n.Graphics.prototype.lineStyle=function(t,e,i){0==this.currentPath.points.length&&this.graphicsData.pop(),this.lineWidth=t||0,this.lineColor=e||0,this.lineAlpha=void 0==i?1:i,this.currentPath={lineWidth:this.lineWidth,lineColor:this.lineColor,lineAlpha:this.lineAlpha,fillColor:this.fillColor,fillAlpha:this.fillAlpha,fill:this.filling,points:[],type:n.Graphics.POLY},this.graphicsData.push(this.currentPath)},n.Graphics.prototype.moveTo=function(t,e){0==this.currentPath.points.length&&this.graphicsData.pop(),this.currentPath=this.currentPath={lineWidth:this.lineWidth,lineColor:this.lineColor,lineAlpha:this.lineAlpha,fillColor:this.fillColor,fillAlpha:this.fillAlpha,fill:this.filling,points:[],type:n.Graphics.POLY},this.currentPath.points.push(t,e),this.graphicsData.push(this.currentPath) -},n.Graphics.prototype.lineTo=function(t,e){this.currentPath.points.push(t,e),this.dirty=!0},n.Graphics.prototype.beginFill=function(t,e){this.filling=!0,this.fillColor=t||0,this.fillAlpha=e||1},n.Graphics.prototype.endFill=function(){this.filling=!1,this.fillColor=null,this.fillAlpha=1},n.Graphics.prototype.drawRect=function(t,e,i,r){0==this.currentPath.points.length&&this.graphicsData.pop(),this.currentPath={lineWidth:this.lineWidth,lineColor:this.lineColor,lineAlpha:this.lineAlpha,fillColor:this.fillColor,fillAlpha:this.fillAlpha,fill:this.filling,points:[t,e,i,r],type:n.Graphics.RECT},this.graphicsData.push(this.currentPath),this.dirty=!0},n.Graphics.prototype.drawCircle=function(t,e,i){0==this.currentPath.points.length&&this.graphicsData.pop(),this.currentPath={lineWidth:this.lineWidth,lineColor:this.lineColor,lineAlpha:this.lineAlpha,fillColor:this.fillColor,fillAlpha:this.fillAlpha,fill:this.filling,points:[t,e,i,i],type:n.Graphics.CIRC},this.graphicsData.push(this.currentPath),this.dirty=!0},n.Graphics.prototype.drawElipse=function(t,e,i,r){0==this.currentPath.points.length&&this.graphicsData.pop(),this.currentPath={lineWidth:this.lineWidth,lineColor:this.lineColor,lineAlpha:this.lineAlpha,fillColor:this.fillColor,fillAlpha:this.fillAlpha,fill:this.filling,points:[t,e,i,r],type:n.Graphics.ELIP},this.graphicsData.push(this.currentPath),this.dirty=!0},n.Graphics.prototype.clear=function(){this.lineWidth=0,this.filling=!1,this.dirty=!0,this.clearDirty=!0,this.graphicsData=[]},n.Graphics.POLY=0,n.Graphics.RECT=1,n.Graphics.CIRC=2,n.Graphics.ELIP=3,n.Strip=function(t,e,i){n.DisplayObjectContainer.call(this),this.texture=t,this.blendMode=n.blendModes.NORMAL;try{this.uvs=new Float32Array([0,1,1,1,1,0,0,1]),this.verticies=new Float32Array([0,0,0,0,0,0,0,0,0]),this.colors=new Float32Array([1,1,1,1]),this.indices=new Uint16Array([0,1,2,3])}catch(r){this.uvs=[0,1,1,1,1,0,0,1],this.verticies=[0,0,0,0,0,0,0,0,0],this.colors=[1,1,1,1],this.indices=[0,1,2,3]}this.width=e,this.height=i,t.baseTexture.hasLoaded?(this.width=this.texture.frame.width,this.height=this.texture.frame.height,this.updateFrame=!0):(this.onTextureUpdateBind=this.onTextureUpdate.bind(this),this.texture.addEventListener("update",this.onTextureUpdateBind)),this.renderable=!0},n.Strip.prototype=Object.create(n.DisplayObjectContainer.prototype),n.Strip.prototype.constructor=n.Strip,n.Strip.prototype.setTexture=function(t){this.texture=t,this.width=t.frame.width,this.height=t.frame.height,this.updateFrame=!0},n.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},n.Rope=function(t,e){n.Strip.call(this,t),this.points=e;try{this.verticies=new Float32Array(4*e.length),this.uvs=new Float32Array(4*e.length),this.colors=new Float32Array(2*e.length),this.indices=new Uint16Array(2*e.length)}catch(i){this.verticies=verticies,this.uvs=uvs,this.colors=colors,this.indices=indices}this.refresh()},n.Rope.prototype=Object.create(n.Strip.prototype),n.Rope.prototype.constructor=n.Rope,n.Rope.prototype.refresh=function(){var t=this.points;if(!(1>t.length)){var e=this.uvs,i=this.indices,r=this.colors,n=t[0],s=t[0];this.count-=.2,e[0]=0,e[1]=1,e[2]=0,e[3]=1,r[0]=1,r[1]=1,i[0]=0,i[1]=1;for(var o=t.length,a=1;o>a;a++){var s=t[a],h=4*a,l=a/(o-1);a%2?(e[h]=l,e[h+1]=0,e[h+2]=l,e[h+3]=1):(e[h]=l,e[h+1]=0,e[h+2]=l,e[h+3]=1),h=2*a,r[h]=1,r[h+1]=1,h=2*a,i[h]=h,i[h+1]=h+1,n=s}}},n.Rope.prototype.updateTransform=function(){var t=this.points;if(!(1>t.length)){var e,i=this.verticies,r=t[0],s={x:0,y:0},o=t[0];this.count-=.2,i[0]=o.x+s.x,i[1]=o.y+s.y,i[2]=o.x-s.x,i[3]=o.y-s.y;for(var a=t.length,h=1;a>h;h++){var o=t[h],l=4*h;e=t.length-1>h?t[h+1]:o,s.y=-(e.x-r.x),s.x=e.y-r.y;var u=10*(1-h/(a-1));u>1&&(u=1);var c=Math.sqrt(s.x*s.x+s.y*s.y),p=this.texture.height/2;s.x/=c,s.y/=c,s.x*=p,s.y*=p,i[l]=o.x+s.x,i[l+1]=o.y+s.y,i[l+2]=o.x-s.x,i[l+3]=o.y-s.y,r=o}n.DisplayObjectContainer.prototype.updateTransform.call(this)}},n.Rope.prototype.setTexture=function(t){this.texture=t,this.updateFrame=!0},n.TilingSprite=function(t,e,i){n.DisplayObjectContainer.call(this),this.texture=t,this.width=e,this.height=i,this.tileScale=new n.Point(1,1),this.tilePosition=new n.Point(0,0),this.renderable=!0,this.blendMode=n.blendModes.NORMAL},n.TilingSprite.prototype=Object.create(n.DisplayObjectContainer.prototype),n.TilingSprite.prototype.constructor=n.TilingSprite,n.TilingSprite.prototype.setTexture=function(t){this.texture=t,this.updateFrame=!0},n.TilingSprite.prototype.onTextureUpdate=function(){this.updateFrame=!0},n.Spine=function(t){if(n.DisplayObjectContainer.call(this),this.spineData=n.AnimCache[t],!this.spineData)throw Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: "+t);this.count=0,this.sprites=[],this.skeleton=new u.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new u.AnimationStateData(this.spineData),this.state=new u.AnimationState(this.stateData);for(var e=0;this.skeleton.drawOrder.length>e;e++){var i=this.skeleton.drawOrder[e].data.attachmentName;n.TextureCache[i]||(i+=".png");var r=new n.Sprite(n.Texture.fromFrame(i));r.anchor.x=r.anchor.y=.5,this.addChild(r),this.sprites.push(r)}},n.Spine.prototype=Object.create(n.DisplayObjectContainer.prototype),n.Spine.prototype.constructor=n.Spine,n.Spine.prototype.updateTransform=function(){this.state.update(1/60),this.state.apply(this.skeleton),this.skeleton.updateWorldTransform();for(var t=0;this.skeleton.drawOrder.length>t;t++){var e=this.skeleton.drawOrder[t],i=e.bone.worldX+e.attachment.x*e.bone.m00+e.attachment.y*e.bone.m01+.5*e.attachment.width,r=e.bone.worldY+e.attachment.x*e.bone.m10+e.attachment.y*e.bone.m11+.5*e.attachment.height;if(e.cacheName!=e.attachment.name){var s=e.attachment.name;n.TextureCache[s]||(s+=".png"),this.sprites[t].setTexture(n.TextureCache[s]),e.cacheName=e.attachment.name}i+=-(e.attachment.width*(e.bone.worldScaleX+e.attachment.scaleX-1)>>1),r+=-(e.attachment.height*(e.bone.worldScaleY+e.attachment.scaleY-1)>>1),this.sprites[t].position.x=i,this.sprites[t].position.y=r,this.sprites[t].rotation=-(e.bone.worldRotation+e.attachment.rotation)*(Math.PI/180)}n.DisplayObjectContainer.prototype.updateTransform.call(this)};var u={};u.BoneData=function(t,e){this.name=t,this.parent=e},u.BoneData.prototype={length:0,x:0,y:0,rotation:0,scaleX:1,scaleY:1},u.SlotData=function(t,e){this.name=t,this.boneData=e},u.SlotData.prototype={r:1,g:1,b:1,a:1,attachmentName:null},u.Bone=function(t,e){this.data=t,this.parent=e,this.setToSetupPose()},u.Bone.yDown=!1,u.Bone.prototype={x:0,y:0,rotation:0,scaleX:1,scaleY:1,m00:0,m01:0,worldX:0,m10:0,m11:0,worldY:0,worldRotation:0,worldScaleX:1,worldScaleY:1,updateWorldTransform:function(t,e){var i=this.parent;null!=i?(this.worldX=this.x*i.m00+this.y*i.m01+i.worldX,this.worldY=this.x*i.m10+this.y*i.m11+i.worldY,this.worldScaleX=i.worldScaleX*this.scaleX,this.worldScaleY=i.worldScaleY*this.scaleY,this.worldRotation=i.worldRotation+this.rotation):(this.worldX=this.x,this.worldY=this.y,this.worldScaleX=this.scaleX,this.worldScaleY=this.scaleY,this.worldRotation=this.rotation);var r=this.worldRotation*Math.PI/180,n=Math.cos(r),s=Math.sin(r);this.m00=n*this.worldScaleX,this.m10=s*this.worldScaleX,this.m01=-s*this.worldScaleY,this.m11=n*this.worldScaleY,t&&(this.m00=-this.m00,this.m01=-this.m01),e&&(this.m10=-this.m10,this.m11=-this.m11),u.Bone.yDown&&(this.m10=-this.m10,this.m11=-this.m11)},setToSetupPose:function(){var t=this.data;this.x=t.x,this.y=t.y,this.rotation=t.rotation,this.scaleX=t.scaleX,this.scaleY=t.scaleY}},u.Slot=function(t,e,i){this.data=t,this.skeleton=e,this.bone=i,this.setToSetupPose()},u.Slot.prototype={r:1,g:1,b:1,a:1,_attachmentTime:0,attachment:null,setAttachment:function(t){this.attachment=t,this._attachmentTime=this.skeleton.time},setAttachmentTime:function(t){this._attachmentTime=this.skeleton.time-t},getAttachmentTime:function(){return this.skeleton.time-this._attachmentTime},setToSetupPose:function(){var t=this.data;this.r=t.r,this.g=t.g,this.b=t.b,this.a=t.a;for(var e=this.skeleton.data.slots,i=0,r=e.length;r>i;i++)if(e[i]==t){this.setAttachment(t.attachmentName?this.skeleton.getAttachmentBySlotIndex(i,t.attachmentName):null);break}}},u.Skin=function(t){this.name=t,this.attachments={}},u.Skin.prototype={addAttachment:function(t,e,i){this.attachments[t+":"+e]=i},getAttachment:function(t,e){return this.attachments[t+":"+e]},_attachAll:function(t,e){for(var i in e.attachments){var r=i.indexOf(":"),n=parseInt(i.substring(0,r)),s=i.substring(r+1),o=t.slots[n];if(o.attachment&&o.attachment.name==s){var a=this.getAttachment(n,s);a&&o.setAttachment(a)}}}},u.Animation=function(t,e,i){this.name=t,this.timelines=e,this.duration=i},u.Animation.prototype={apply:function(t,e,i){i&&0!=this.duration&&(e%=this.duration);for(var r=this.timelines,n=0,s=r.length;s>n;n++)r[n].apply(t,e,1)},mix:function(t,e,i,r){i&&0!=this.duration&&(e%=this.duration);for(var n=this.timelines,s=0,o=n.length;o>s;s++)n[s].apply(t,e,r)}},u.binarySearch=function(t,e,i){var r=0,n=Math.floor(t.length/i)-2;if(0==n)return i;for(var s=n>>>1;;){if(e>=t[(s+1)*i]?r=s+1:n=s,r==n)return(r+1)*i;s=r+n>>>1}},u.linearSearch=function(t,e,i){for(var r=0,n=t.length-i;n>=r;r+=i)if(t[r]>e)return r;return-1},u.Curves=function(t){this.curves=[],this.curves.length=6*(t-1)},u.Curves.prototype={setLinear:function(t){this.curves[6*t]=0},setStepped:function(t){this.curves[6*t]=-1},setCurve:function(t,e,i,r,n){var s=.1,o=s*s,a=o*s,h=3*s,l=3*o,u=6*o,c=6*a,p=2*-e+r,d=2*-i+n,f=3*(e-r)+1,v=3*(i-n)+1,m=6*t,g=this.curves;g[m]=e*h+p*l+f*a,g[m+1]=i*h+d*l+v*a,g[m+2]=p*u+f*c,g[m+3]=d*u+v*c,g[m+4]=f*c,g[m+5]=v*c},getCurvePercent:function(t,e){e=0>e?0:e>1?1:e;var i=6*t,r=this.curves,n=r[i];if(!n)return e;if(-1==n)return 0;for(var s=r[i+1],o=r[i+2],a=r[i+3],h=r[i+4],l=r[i+5],u=n,c=s,p=8;;){if(u>=e){var d=u-n,f=c-s;return f+(c-f)*(e-d)/(u-d)}if(0==p)break;p--,n+=o,s+=a,o+=h,a+=l,u+=n,c+=s}return c+(1-c)*(e-u)/(1-u)}},u.RotateTimeline=function(t){this.curves=new u.Curves(t),this.frames=[],this.frames.length=2*t},u.RotateTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/2},setFrame:function(t,e,i){t*=2,this.frames[t]=e,this.frames[t+1]=i},apply:function(t,e,i){var r=this.frames;if(!(r[0]>e)){var n=t.bones[this.boneIndex];if(e>=r[r.length-2]){for(var s=n.data.rotation+r[r.length-1]-n.rotation;s>180;)s-=360;for(;-180>s;)s+=360;return n.rotation+=s*i,void 0}var o=u.binarySearch(r,e,2),a=r[o-1],h=r[o],l=1-(e-h)/(r[o-2]-h);l=this.curves.getCurvePercent(o/2-1,l);for(var s=r[o+1]-a;s>180;)s-=360;for(;-180>s;)s+=360;for(s=n.data.rotation+(a+s*l)-n.rotation;s>180;)s-=360;for(;-180>s;)s+=360;n.rotation+=s*i}}},u.TranslateTimeline=function(t){this.curves=new u.Curves(t),this.frames=[],this.frames.length=3*t},u.TranslateTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/3},setFrame:function(t,e,i,r){t*=3,this.frames[t]=e,this.frames[t+1]=i,this.frames[t+2]=r},apply:function(t,e,i){var r=this.frames;if(!(r[0]>e)){var n=t.bones[this.boneIndex];if(e>=r[r.length-3])return n.x+=(n.data.x+r[r.length-2]-n.x)*i,n.y+=(n.data.y+r[r.length-1]-n.y)*i,void 0;var s=u.binarySearch(r,e,3),o=r[s-2],a=r[s-1],h=r[s],l=1-(e-h)/(r[s+-3]-h);l=this.curves.getCurvePercent(s/3-1,l),n.x+=(n.data.x+o+(r[s+1]-o)*l-n.x)*i,n.y+=(n.data.y+a+(r[s+2]-a)*l-n.y)*i}}},u.ScaleTimeline=function(t){this.curves=new u.Curves(t),this.frames=[],this.frames.length=3*t},u.ScaleTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/3},setFrame:function(t,e,i,r){t*=3,this.frames[t]=e,this.frames[t+1]=i,this.frames[t+2]=r},apply:function(t,e,i){var r=this.frames;if(!(r[0]>e)){var n=t.bones[this.boneIndex];if(e>=r[r.length-3])return n.scaleX+=(n.data.scaleX-1+r[r.length-2]-n.scaleX)*i,n.scaleY+=(n.data.scaleY-1+r[r.length-1]-n.scaleY)*i,void 0;var s=u.binarySearch(r,e,3),o=r[s-2],a=r[s-1],h=r[s],l=1-(e-h)/(r[s+-3]-h);l=this.curves.getCurvePercent(s/3-1,l),n.scaleX+=(n.data.scaleX-1+o+(r[s+1]-o)*l-n.scaleX)*i,n.scaleY+=(n.data.scaleY-1+a+(r[s+2]-a)*l-n.scaleY)*i}}},u.ColorTimeline=function(t){this.curves=new u.Curves(t),this.frames=[],this.frames.length=5*t},u.ColorTimeline.prototype={slotIndex:0,getFrameCount:function(){return this.frames.length/2},setFrame:function(t,e){t*=5,this.frames[t]=e,this.frames[t+1]=r,this.frames[t+2]=g,this.frames[t+3]=b,this.frames[t+4]=a},apply:function(t,e,i){var r=this.frames;if(!(r[0]>e)){var n=t.slots[this.slotIndex];if(e>=r[r.length-5]){var s=r.length-1;return n.r=r[s-3],n.g=r[s-2],n.b=r[s-1],n.a=r[s],void 0}var o=u.binarySearch(r,e,5),a=r[o-4],h=r[o-3],l=r[o-2],c=r[o-1],p=r[o],d=1-(e-p)/(r[o-5]-p);d=this.curves.getCurvePercent(o/5-1,d);var f=a+(r[o+1]-a)*d,v=h+(r[o+2]-h)*d,m=l+(r[o+3]-l)*d,g=c+(r[o+4]-c)*d;1>i?(n.r+=(f-n.r)*i,n.g+=(v-n.g)*i,n.b+=(m-n.b)*i,n.a+=(g-n.a)*i):(n.r=f,n.g=v,n.b=m,n.a=g)}}},u.AttachmentTimeline=function(t){this.curves=new u.Curves(t),this.frames=[],this.frames.length=t,this.attachmentNames=[],this.attachmentNames.length=t},u.AttachmentTimeline.prototype={slotIndex:0,getFrameCount:function(){return this.frames.length/2},setFrame:function(t,e,i){this.frames[t]=e,this.attachmentNames[t]=i},apply:function(t,e){var i=this.frames;if(!(i[0]>e)){var r;r=e>=i[i.length-1]?i.length-1:u.binarySearch(i,e,1)-1;var n=this.attachmentNames[r];t.slots[this.slotIndex].setAttachment(n?t.getAttachmentBySlotIndex(this.slotIndex,n):null)}}},u.SkeletonData=function(){this.bones=[],this.slots=[],this.skins=[],this.animations=[]},u.SkeletonData.prototype={defaultSkin:null,findBone:function(t){for(var e=this.bones,i=0,r=e.length;r>i;i++)if(e[i].name==t)return e[i];return null},findBoneIndex:function(t){for(var e=this.bones,i=0,r=e.length;r>i;i++)if(e[i].name==t)return i;return-1},findSlot:function(t){for(var e=this.slots,i=0,r=e.length;r>i;i++)if(e[i].name==t)return slot[i];return null},findSlotIndex:function(t){for(var e=this.slots,i=0,r=e.length;r>i;i++)if(e[i].name==t)return i;return-1},findSkin:function(t){for(var e=this.skins,i=0,r=e.length;r>i;i++)if(e[i].name==t)return e[i];return null},findAnimation:function(t){for(var e=this.animations,i=0,r=e.length;r>i;i++)if(e[i].name==t)return e[i];return null}},u.Skeleton=function(t){this.data=t,this.bones=[];for(var e=0,i=t.bones.length;i>e;e++){var r=t.bones[e],n=r.parent?this.bones[t.bones.indexOf(r.parent)]:null;this.bones.push(new u.Bone(r,n))}this.slots=[],this.drawOrder=[];for(var e=0,i=t.slots.length;i>e;e++){var s=t.slots[e],o=this.bones[t.bones.indexOf(s.boneData)],a=new u.Slot(s,this,o);this.slots.push(a),this.drawOrder.push(a)}},u.Skeleton.prototype={x:0,y:0,skin:null,r:1,g:1,b:1,a:1,time:0,flipX:!1,flipY:!1,updateWorldTransform:function(){for(var t=this.flipX,e=this.flipY,i=this.bones,r=0,n=i.length;n>r;r++)i[r].updateWorldTransform(t,e)},setToSetupPose:function(){this.setBonesToSetupPose(),this.setSlotsToSetupPose()},setBonesToSetupPose:function(){for(var t=this.bones,e=0,i=t.length;i>e;e++)t[e].setToSetupPose()},setSlotsToSetupPose:function(){for(var t=this.slots,e=0,i=t.length;i>e;e++)t[e].setToSetupPose(e)},getRootBone:function(){return 0==this.bones.length?null:this.bones[0]},findBone:function(t){for(var e=this.bones,i=0,r=e.length;r>i;i++)if(e[i].data.name==t)return e[i];return null},findBoneIndex:function(t){for(var e=this.bones,i=0,r=e.length;r>i;i++)if(e[i].data.name==t)return i;return-1},findSlot:function(t){for(var e=this.slots,i=0,r=e.length;r>i;i++)if(e[i].data.name==t)return e[i];return null},findSlotIndex:function(t){for(var e=this.slots,i=0,r=e.length;r>i;i++)if(e[i].data.name==t)return i;return-1},setSkinByName:function(t){var e=this.data.findSkin(t);if(!e)throw"Skin not found: "+t;this.setSkin(e)},setSkin:function(t){this.skin&&t&&t._attachAll(this,this.skin),this.skin=t},getAttachmentBySlotName:function(t,e){return this.getAttachmentBySlotIndex(this.data.findSlotIndex(t),e)},getAttachmentBySlotIndex:function(t,e){if(this.skin){var i=this.skin.getAttachment(t,e);if(i)return i}return this.data.defaultSkin?this.data.defaultSkin.getAttachment(t,e):null},setAttachment:function(t,e){for(var i=this.slots,r=0,n=i.size;n>r;r++){var s=i[r];if(s.data.name==t){var o=null;if(e&&(o=this.getAttachment(r,e),null==o))throw"Attachment not found: "+e+", for slot: "+t;return s.setAttachment(o),void 0}}throw"Slot not found: "+t},update:function(t){time+=t}},u.AttachmentType={region:0},u.RegionAttachment=function(){this.offset=[],this.offset.length=8,this.uvs=[],this.uvs.length=8},u.RegionAttachment.prototype={x:0,y:0,rotation:0,scaleX:1,scaleY:1,width:0,height:0,rendererObject:null,regionOffsetX:0,regionOffsetY:0,regionWidth:0,regionHeight:0,regionOriginalWidth:0,regionOriginalHeight:0,setUVs:function(t,e,i,r,n){var s=this.uvs;n?(s[2]=t,s[3]=r,s[4]=t,s[5]=e,s[6]=i,s[7]=e,s[0]=i,s[1]=r):(s[0]=t,s[1]=r,s[2]=t,s[3]=e,s[4]=i,s[5]=e,s[6]=i,s[7]=r)},updateOffset:function(){var t=this.width/this.regionOriginalWidth*this.scaleX,e=this.height/this.regionOriginalHeight*this.scaleY,i=-this.width/2*this.scaleX+this.regionOffsetX*t,r=-this.height/2*this.scaleY+this.regionOffsetY*e,n=i+this.regionWidth*t,s=r+this.regionHeight*e,o=this.rotation*Math.PI/180,a=Math.cos(o),h=Math.sin(o),l=i*a+this.x,u=i*h,c=r*a+this.y,p=r*h,d=n*a+this.x,f=n*h,v=s*a+this.y,m=s*h,g=this.offset;g[0]=l-p,g[1]=c+u,g[2]=l-m,g[3]=v+u,g[4]=d-m,g[5]=v+f,g[6]=d-p,g[7]=c+f},computeVertices:function(t,e,i,r){t+=i.worldX,e+=i.worldY;var n=i.m00,s=i.m01,o=i.m10,a=i.m11,h=this.offset;r[0]=h[0]*n+h[1]*s+t,r[1]=h[0]*o+h[1]*a+e,r[2]=h[2]*n+h[3]*s+t,r[3]=h[2]*o+h[3]*a+e,r[4]=h[4]*n+h[5]*s+t,r[5]=h[4]*o+h[5]*a+e,r[6]=h[6]*n+h[7]*s+t,r[7]=h[6]*o+h[7]*a+e}},u.AnimationStateData=function(t){this.skeletonData=t,this.animationToMixTime={}},u.AnimationStateData.prototype={setMixByName:function(t,e,i){var r=this.skeletonData.findAnimation(t);if(!r)throw"Animation not found: "+t;var n=this.skeletonData.findAnimation(e);if(!n)throw"Animation not found: "+e;this.setMix(r,n,i)},setMix:function(t,e,i){this.animationToMixTime[t.name+":"+e.name]=i},getMix:function(t,e){var i=this.animationToMixTime[t.name+":"+e.name];return i?i:0}},u.AnimationState=function(t){this.data=t,this.queue=[]},u.AnimationState.prototype={current:null,previous:null,currentTime:0,previousTime:0,currentLoop:!1,previousLoop:!1,mixTime:0,mixDuration:0,update:function(t){if(this.currentTime+=t,this.previousTime+=t,this.mixTime+=t,this.queue.length>0){var e=this.queue[0];this.currentTime>=e.delay&&(this._setAnimation(e.animation,e.loop),this.queue.shift())}},apply:function(t){if(this.current)if(this.previous){this.previous.apply(t,this.previousTime,this.previousLoop);var e=this.mixTime/this.mixDuration;e>=1&&(e=1,this.previous=null),this.current.mix(t,this.currentTime,this.currentLoop,e)}else this.current.apply(t,this.currentTime,this.currentLoop)},clearAnimation:function(){this.previous=null,this.current=null,this.queue.length=0},_setAnimation:function(t,e){this.previous=null,t&&this.current&&(this.mixDuration=this.data.getMix(this.current,t),this.mixDuration>0&&(this.mixTime=0,this.previous=this.current,this.previousTime=this.currentTime,this.previousLoop=this.currentLoop)),this.current=t,this.currentLoop=e,this.currentTime=0},setAnimationByName:function(t,e){var i=this.data.skeletonData.findAnimation(t);if(!i)throw"Animation not found: "+t;this.setAnimation(i,e)},setAnimation:function(t,e){this.queue.length=0,this._setAnimation(t,e)},addAnimationByName:function(t,e,i){var r=this.data.skeletonData.findAnimation(t);if(!r)throw"Animation not found: "+t;this.addAnimation(r,e,i)},addAnimation:function(t,e,i){var r={};if(r.animation=t,r.loop=e,!i||0>=i){var n=0==this.queue.length?this.current:this.queue[this.queue.length-1].animation;i=null!=n?n.duration-this.data.getMix(n,t)+(i||0):0}r.delay=i,this.queue.push(r)},isComplete:function(){return!this.current||this.currentTime>=this.current.duration}},u.SkeletonJson=function(t){this.attachmentLoader=t},u.SkeletonJson.prototype={scale:1,readSkeletonData:function(t){for(var e=new u.SkeletonData,i=t.bones,r=0,n=i.length;n>r;r++){var s=i[r],o=null;if(s.parent&&(o=e.findBone(s.parent),!o))throw"Parent bone not found: "+s.parent;var a=new u.BoneData(s.name,o);a.length=(s.length||0)*this.scale,a.x=(s.x||0)*this.scale,a.y=(s.y||0)*this.scale,a.rotation=s.rotation||0,a.scaleX=s.scaleX||1,a.scaleY=s.scaleY||1,e.bones.push(a)}for(var h=t.slots,r=0,n=h.length;n>r;r++){var l=h[r],a=e.findBone(l.bone);if(!a)throw"Slot bone not found: "+l.bone;var c=new u.SlotData(l.name,a),p=l.color;p&&(c.r=u.SkeletonJson.toColor(p,0),c.g=u.SkeletonJson.toColor(p,1),c.b=u.SkeletonJson.toColor(p,2),c.a=u.SkeletonJson.toColor(p,3)),c.attachmentName=l.attachment,e.slots.push(c)}var d=t.skins;for(var f in d)if(d.hasOwnProperty(f)){var v=d[f],m=new u.Skin(f);for(var g in v)if(v.hasOwnProperty(g)){var x=e.findSlotIndex(g),b=v[g];for(var y in b)if(b.hasOwnProperty(y)){var T=this.readAttachment(m,y,b[y]);null!=T&&m.addAttachment(x,y,T)}}e.skins.push(m),"default"==m.name&&(e.defaultSkin=m)}var _=t.animations;for(var w in _)_.hasOwnProperty(w)&&this.readAnimation(w,_[w],e);return e},readAttachment:function(t,e,i){e=i.name||e;var r=u.AttachmentType[i.type||"region"],n=new u.RegionAttachment;return n.name=e,r==u.AttachmentType.region&&(n.x=(i.x||0)*this.scale,n.y=(i.y||0)*this.scale,n.scaleX=i.scaleX||1,n.scaleY=i.scaleY||1,n.rotation=i.rotation||0,n.width=(i.width||32)*this.scale,n.height=(i.height||32)*this.scale,n.updateOffset()),n},readAnimation:function(t,e,i){var r=[],n=0,s=e.bones;for(var o in s)if(s.hasOwnProperty(o)){var a=i.findBoneIndex(o);if(-1==a)throw"Bone not found: "+o;var h=s[o];for(var l in h)if(h.hasOwnProperty(l)){var c=h[l];if("rotate"==l){var p=new u.RotateTimeline(c.length);p.boneIndex=a;for(var d=0,f=0,v=c.length;v>f;f++){var m=c[f];p.setFrame(d,m.time,m.angle),u.SkeletonJson.readCurve(p,d,m),d++}r.push(p),n=Math.max(n,p.frames[2*p.getFrameCount()-2])}else{if("translate"!=l&&"scale"!=l)throw"Invalid timeline type for a bone: "+l+" ("+o+")";var p,g=1;"scale"==l?p=new u.ScaleTimeline(c.length):(p=new u.TranslateTimeline(c.length),g=this.scale),p.boneIndex=a;for(var d=0,f=0,v=c.length;v>f;f++){var m=c[f],x=(m.x||0)*g,b=(m.y||0)*g;p.setFrame(d,m.time,x,b),u.SkeletonJson.readCurve(p,d,m),d++}r.push(p),n=Math.max(n,p.frames[3*p.getFrameCount()-3])}}}var y=e.slots;for(var T in y)if(y.hasOwnProperty(T)){var _=y[T],w=i.findSlotIndex(T);for(var l in _)if(_.hasOwnProperty(l)){var c=_[l];if("color"==l){var p=new u.ColorTimeline(c.length);p.slotIndex=w;for(var d=0,f=0,v=c.length;v>f;f++){var m=c[f],A=m.color,R=u.SkeletonJson.toColor(A,0),S=u.SkeletonJson.toColor(A,1),C=u.SkeletonJson.toColor(A,2),L=u.SkeletonJson.toColor(A,3);p.setFrame(d,m.time,R,S,C,L),u.SkeletonJson.readCurve(p,d,m),d++}r.push(p),n=Math.max(n,p.frames[5*p.getFrameCount()-5])}else{if("attachment"!=l)throw"Invalid timeline type for a slot: "+l+" ("+T+")";var p=new u.AttachmentTimeline(c.length);p.slotIndex=w;for(var d=0,f=0,v=c.length;v>f;f++){var m=c[f];p.setFrame(d++,m.time,m.name)}r.push(p),n=Math.max(n,p.frames[Math.floor(p.getFrameCount())-1])}}}i.animations.push(new u.Animation(t,r,n))}},u.SkeletonJson.readCurve=function(t,e,i){var r=i.curve;r&&("stepped"==r?t.curves.setStepped(e):r instanceof Array&&t.curves.setCurve(e,r[0],r[1],r[2],r[3]))},u.SkeletonJson.toColor=function(t,e){if(8!=t.length)throw"Color hexidecimal length must be 8, recieved: "+t;return parseInt(t.substring(2*e,2),16)/255},u.Atlas=function(t,e){this.textureLoader=e,this.pages=[],this.regions=[];var i=new u.AtlasReader(t),r=[];r.length=4;for(var n=null;;){var s=i.readLine();if(null==s)break;if(s=i.trim(s),0==s.length)n=null;else if(n){var o=new u.AtlasRegion;o.name=s,o.page=n,o.rotate="true"==i.readValue(),i.readTuple(r);var a=parseInt(r[0]),h=parseInt(r[1]);i.readTuple(r);var l=parseInt(r[0]),c=parseInt(r[1]);o.u=a/n.width,o.v=h/n.height,o.rotate?(o.u2=(a+c)/n.width,o.v2=(h+l)/n.height):(o.u2=(a+l)/n.width,o.v2=(h+c)/n.height),o.x=a,o.y=h,o.width=Math.abs(l),o.height=Math.abs(c),4==i.readTuple(r)&&(o.splits=[parseInt(r[0]),parseInt(r[1]),parseInt(r[2]),parseInt(r[3])],4==i.readTuple(r)&&(o.pads=[parseInt(r[0]),parseInt(r[1]),parseInt(r[2]),parseInt(r[3])],i.readTuple(r))),o.originalWidth=parseInt(r[0]),o.originalHeight=parseInt(r[1]),i.readTuple(r),o.offsetX=parseInt(r[0]),o.offsetY=parseInt(r[1]),o.index=parseInt(i.readValue()),this.regions.push(o)}else{n=new u.AtlasPage,n.name=s,n.format=u.Atlas.Format[i.readValue()],i.readTuple(r),n.minFilter=u.Atlas.TextureFilter[r[0]],n.magFilter=u.Atlas.TextureFilter[r[1]];var p=i.readValue();n.uWrap=u.Atlas.TextureWrap.clampToEdge,n.vWrap=u.Atlas.TextureWrap.clampToEdge,"x"==p?n.uWrap=u.Atlas.TextureWrap.repeat:"y"==p?n.vWrap=u.Atlas.TextureWrap.repeat:"xy"==p&&(n.uWrap=n.vWrap=u.Atlas.TextureWrap.repeat),e.load(n,s),this.pages.push(n)}}},u.Atlas.prototype={findRegion:function(t){for(var e=this.regions,i=0,r=e.length;r>i;i++)if(e[i].name==t)return e[i];return null},dispose:function(){for(var t=this.pages,e=0,i=t.length;i>e;e++)this.textureLoader.unload(t[e].rendererObject)},updateUVs:function(t){for(var e=this.regions,i=0,r=e.length;r>i;i++){var n=e[i];n.page==t&&(n.u=n.x/t.width,n.v=n.y/t.height,n.rotate?(n.u2=(n.x+n.height)/t.width,n.v2=(n.y+n.width)/t.height):(n.u2=(n.x+n.width)/t.width,n.v2=(n.y+n.height)/t.height))}}},u.Atlas.Format={alpha:0,intensity:1,luminanceAlpha:2,rgb565:3,rgba4444:4,rgb888:5,rgba8888:6},u.Atlas.TextureFilter={nearest:0,linear:1,mipMap:2,mipMapNearestNearest:3,mipMapLinearNearest:4,mipMapNearestLinear:5,mipMapLinearLinear:6},u.Atlas.TextureWrap={mirroredRepeat:0,clampToEdge:1,repeat:2},u.AtlasPage=function(){},u.AtlasPage.prototype={name:null,format:null,minFilter:null,magFilter:null,uWrap:null,vWrap:null,rendererObject:null,width:0,height:0},u.AtlasRegion=function(){},u.AtlasRegion.prototype={page:null,name:null,x:0,y:0,width:0,height:0,u:0,v:0,u2:0,v2:0,offsetX:0,offsetY:0,originalWidth:0,originalHeight:0,index:0,rotate:!1,splits:null,pads:null},u.AtlasReader=function(t){this.lines=t.split(/\r\n|\r|\n/)},u.AtlasReader.prototype={index:0,trim:function(t){return t.replace(/^\s+|\s+$/g,"")},readLine:function(){return this.index>=this.lines.length?null:this.lines[this.index++]},readValue:function(){var t=this.readLine(),e=t.indexOf(":");if(-1==e)throw"Invalid line: "+t;return this.trim(t.substring(e+1))},readTuple:function(t){var e=this.readLine(),i=e.indexOf(":");if(-1==i)throw"Invalid line: "+e;for(var r=0,n=i+1;3>r;r++){var s=e.indexOf(",",n);if(-1==s){if(0==r)throw"Invalid line: "+e;break}t[r]=this.trim(e.substr(n,s-n)),n=s+1}return t[r]=this.trim(e.substring(n)),r+1}},u.AtlasAttachmentLoader=function(t){this.atlas=t},u.AtlasAttachmentLoader.prototype={newAttachment:function(t,e,i){switch(e){case u.AttachmentType.region:var r=this.atlas.findRegion(i);if(!r)throw"Region not found in atlas: "+i+" ("+e+")";var n=new u.RegionAttachment(i);return n.rendererObject=r,n.setUVs(r.u,r.v,r.u2,r.v2,r.rotate),n.regionOffsetX=r.offsetX,n.regionOffsetY=r.offsetY,n.regionWidth=r.width,n.regionHeight=r.height,n.regionOriginalWidth=r.originalWidth,n.regionOriginalHeight=r.originalHeight,n}throw"Unknown attachment type: "+e}},n.AnimCache={},u.Bone.yDown=!0,n.CustomRenderable=function(){n.DisplayObject.call(this)},n.CustomRenderable.prototype=Object.create(n.DisplayObject.prototype),n.CustomRenderable.prototype.constructor=n.CustomRenderable,n.CustomRenderable.prototype.renderCanvas=function(){},n.CustomRenderable.prototype.initWebGL=function(){},n.CustomRenderable.prototype.renderWebGL=function(){},n.BaseTextureCache={},n.texturesToUpdate=[],n.texturesToDestroy=[],n.BaseTexture=function(t){if(n.EventTarget.call(this),this.width=100,this.height=100,this.hasLoaded=!1,this.source=t,t){if(this.source instanceof Image||this.source instanceof HTMLImageElement)if(this.source.complete)this.hasLoaded=!0,this.width=this.source.width,this.height=this.source.height,n.texturesToUpdate.push(this);else{var e=this;this.source.onload=function(){e.hasLoaded=!0,e.width=e.source.width,e.height=e.source.height,n.texturesToUpdate.push(e),e.dispatchEvent({type:"loaded",content:e})}}else this.hasLoaded=!0,this.width=this.source.width,this.height=this.source.height,n.texturesToUpdate.push(this);this._powerOf2=!1}},n.BaseTexture.prototype.constructor=n.BaseTexture,n.BaseTexture.prototype.destroy=function(){this.source instanceof Image&&(this.source.src=null),this.source=null,n.texturesToDestroy.push(this)},n.BaseTexture.fromImage=function(t,e){var i=n.BaseTextureCache[t];if(!i){var r=new Image;e&&(r.crossOrigin=""),r.src=t,i=new n.BaseTexture(r),n.BaseTextureCache[t]=i}return i},n.TextureCache={},n.FrameCache={},n.Texture=function(t,e){if(n.EventTarget.call(this),e||(this.noFrame=!0,e=new n.Rectangle(0,0,1,1)),t instanceof n.Texture&&(t=t.baseTexture),this.baseTexture=t,this.frame=e,this.trim=new n.Point,this.scope=this,t.hasLoaded)this.noFrame&&(e=new n.Rectangle(0,0,t.width,t.height)),this.setFrame(e);else{var i=this;t.addEventListener("loaded",function(){i.onBaseTextureLoaded()})}},n.Texture.prototype.constructor=n.Texture,n.Texture.prototype.onBaseTextureLoaded=function(){var t=this.baseTexture;t.removeEventListener("loaded",this.onLoaded),this.noFrame&&(this.frame=new n.Rectangle(0,0,t.width,t.height)),this.noFrame=!1,this.width=this.frame.width,this.height=this.frame.height,this.scope.dispatchEvent({type:"update",content:this})},n.Texture.prototype.destroy=function(t){t&&this.baseTexture.destroy()},n.Texture.prototype.setFrame=function(t){if(this.frame=t,this.width=t.width,this.height=t.height,t.x+t.width>this.baseTexture.width||t.y+t.height>this.baseTexture.height)throw Error("Texture Error: frame does not fit inside the base Texture dimensions "+this);this.updateFrame=!0,n.Texture.frameUpdates.push(this)},n.Texture.fromImage=function(t,e){var i=n.TextureCache[t];return i||(i=new n.Texture(n.BaseTexture.fromImage(t,e)),n.TextureCache[t]=i),i},n.Texture.fromFrame=function(t){var e=n.TextureCache[t];if(!e)throw Error("The frameId '"+t+"' does not exist in the texture cache "+this);return e},n.Texture.fromCanvas=function(t){var e=new n.BaseTexture(t);return new n.Texture(e)},n.Texture.addTextureToCache=function(t,e){n.TextureCache[e]=t},n.Texture.removeTextureFromCache=function(t){var e=n.TextureCache[t];return n.TextureCache[t]=null,e},n.Texture.frameUpdates=[],n.RenderTexture=function(t,e){n.EventTarget.call(this),this.width=t||100,this.height=e||100,this.indetityMatrix=n.mat3.create(),this.frame=new n.Rectangle(0,0,this.width,this.height),n.gl?this.initWebGL():this.initCanvas()},n.RenderTexture.prototype=Object.create(n.Texture.prototype),n.RenderTexture.prototype.constructor=n.RenderTexture,n.RenderTexture.prototype.initWebGL=function(){var t=n.gl;this.glFramebuffer=t.createFramebuffer(),t.bindFramebuffer(t.FRAMEBUFFER,this.glFramebuffer),this.glFramebuffer.width=this.width,this.glFramebuffer.height=this.height,this.baseTexture=new n.BaseTexture,this.baseTexture.width=this.width,this.baseTexture.height=this.height,this.baseTexture._glTexture=t.createTexture(),t.bindTexture(t.TEXTURE_2D,this.baseTexture._glTexture),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,this.width,this.height,0,t.RGBA,t.UNSIGNED_BYTE,null),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.LINEAR),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),this.baseTexture.isRender=!0,t.bindFramebuffer(t.FRAMEBUFFER,this.glFramebuffer),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,this.baseTexture._glTexture,0),this.projection=new n.Point(this.width/2,this.height/2),this.render=this.renderWebGL},n.RenderTexture.prototype.resize=function(t,e){if(this.width=t,this.height=e,n.gl){this.projection.x=this.width/2,this.projection.y=this.height/2;var i=n.gl;i.bindTexture(i.TEXTURE_2D,this.baseTexture._glTexture),i.texImage2D(i.TEXTURE_2D,0,i.RGBA,this.width,this.height,0,i.RGBA,i.UNSIGNED_BYTE,null)}else this.frame.width=this.width,this.frame.height=this.height,this.renderer.resize(this.width,this.height) -},n.RenderTexture.prototype.initCanvas=function(){this.renderer=new n.CanvasRenderer(this.width,this.height,null,0),this.baseTexture=new n.BaseTexture(this.renderer.view),this.frame=new n.Rectangle(0,0,this.width,this.height),this.render=this.renderCanvas},n.RenderTexture.prototype.renderWebGL=function(t,e,i){var r=n.gl;r.colorMask(!0,!0,!0,!0),r.viewport(0,0,this.width,this.height),r.bindFramebuffer(r.FRAMEBUFFER,this.glFramebuffer),i&&(r.clearColor(0,0,0,0),r.clear(r.COLOR_BUFFER_BIT));var s=t.children;t.worldTransform=n.mat3.create(),t.worldTransform[4]=-1,t.worldTransform[5]=2*this.projection.y,e&&(t.worldTransform[2]=e.x,t.worldTransform[5]-=e.y);for(var o=0,a=s.length;a>o;o++)s[o].updateTransform();var h=t.__renderGroup;h?t==h.root?h.render(this.projection):h.renderSpecific(t,this.projection):(this.renderGroup||(this.renderGroup=new n.WebGLRenderGroup(r)),this.renderGroup.setRenderable(t),this.renderGroup.render(this.projection))},n.RenderTexture.prototype.renderCanvas=function(t,e,i){var r=t.children;t.worldTransform=n.mat3.create(),e&&(t.worldTransform[2]=e.x,t.worldTransform[5]=e.y);for(var s=0,o=r.length;o>s;s++)r[s].updateTransform();i&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(t),this.renderer.context.setTransform(1,0,0,1,0,0)},n.AssetLoader=function(t,e){n.EventTarget.call(this),this.assetURLs=t,this.crossorigin=e,this.loadersByType={jpg:n.ImageLoader,jpeg:n.ImageLoader,png:n.ImageLoader,gif:n.ImageLoader,json:n.JsonLoader,anim:n.SpineLoader,xml:n.BitmapFontLoader,fnt:n.BitmapFontLoader}},n.AssetLoader.prototype.constructor=n.AssetLoader,n.AssetLoader.prototype.load=function(){var t=this;this.loadCount=this.assetURLs.length;for(var e=0;this.assetURLs.length>e;e++){var i=this.assetURLs[e],r=i.split(".").pop().toLowerCase(),n=this.loadersByType[r];if(!n)throw Error(r+" is an unsupported file type");var s=new n(i,this.crossorigin);s.addEventListener("loaded",function(){t.onAssetLoaded()}),s.load()}},n.AssetLoader.prototype.onAssetLoaded=function(){this.loadCount--,this.dispatchEvent({type:"onProgress",content:this}),this.onProgress&&this.onProgress(),0==this.loadCount&&(this.dispatchEvent({type:"onComplete",content:this}),this.onComplete&&this.onComplete())},n.JsonLoader=function(t,e){n.EventTarget.call(this),this.url=t,this.crossorigin=e,this.baseUrl=t.replace(/[^\/]*$/,""),this.loaded=!1},n.JsonLoader.prototype.constructor=n.JsonLoader,n.JsonLoader.prototype.load=function(){this.ajaxRequest=new l;var t=this;this.ajaxRequest.onreadystatechange=function(){t.onJSONLoaded()},this.ajaxRequest.open("GET",this.url,!0),this.ajaxRequest.overrideMimeType&&this.ajaxRequest.overrideMimeType("application/json"),this.ajaxRequest.send(null)},n.JsonLoader.prototype.onJSONLoaded=function(){if(4==this.ajaxRequest.readyState)if(200==this.ajaxRequest.status||-1==window.location.href.indexOf("http"))if(this.json=JSON.parse(this.ajaxRequest.responseText),this.json.frames){var t=this,e=this.baseUrl+this.json.meta.image,i=new n.ImageLoader(e,this.crossorigin),r=this.json.frames;this.texture=i.texture.baseTexture,i.addEventListener("loaded",function(){t.onLoaded()});for(var s in r){var o=r[s].frame;o&&(n.TextureCache[s]=new n.Texture(this.texture,{x:o.x,y:o.y,width:o.w,height:o.h}),r[s].trimmed&&(n.TextureCache[s].realSize=r[s].spriteSourceSize,n.TextureCache[s].trim.x=0))}i.load()}else if(this.json.bones){var a=new u.SkeletonJson,h=a.readSkeletonData(this.json);n.AnimCache[this.url]=h,this.onLoaded()}else this.onLoaded();else this.onError()},n.JsonLoader.prototype.onLoaded=function(){this.loaded=!0,this.dispatchEvent({type:"loaded",content:this})},n.JsonLoader.prototype.onError=function(){this.dispatchEvent({type:"error",content:this})},n.SpriteSheetLoader=function(t,e){n.EventTarget.call(this),this.url=t,this.crossorigin=e,this.baseUrl=t.replace(/[^\/]*$/,""),this.texture=null,this.frames={}},n.SpriteSheetLoader.prototype.constructor=n.SpriteSheetLoader,n.SpriteSheetLoader.prototype.load=function(){var t=this,e=new n.JsonLoader(this.url,this.crossorigin);e.addEventListener("loaded",function(e){t.json=e.content.json,t.onJSONLoaded()}),e.load()},n.SpriteSheetLoader.prototype.onJSONLoaded=function(){var t=this,e=this.baseUrl+this.json.meta.image,i=new n.ImageLoader(e,this.crossorigin),r=this.json.frames;this.texture=i.texture.baseTexture,i.addEventListener("loaded",function(){t.onLoaded()});for(var s in r){var o=r[s].frame;o&&(n.TextureCache[s]=new n.Texture(this.texture,{x:o.x,y:o.y,width:o.w,height:o.h}),r[s].trimmed&&(n.TextureCache[s].realSize=r[s].spriteSourceSize,n.TextureCache[s].trim.x=0))}i.load()},n.SpriteSheetLoader.prototype.onLoaded=function(){this.dispatchEvent({type:"loaded",content:this})},n.ImageLoader=function(t,e){n.EventTarget.call(this),this.texture=n.Texture.fromImage(t,e)},n.ImageLoader.prototype.constructor=n.ImageLoader,n.ImageLoader.prototype.load=function(){if(this.texture.baseTexture.hasLoaded)this.onLoaded();else{var t=this;this.texture.baseTexture.addEventListener("loaded",function(){t.onLoaded()})}},n.ImageLoader.prototype.onLoaded=function(){this.dispatchEvent({type:"loaded",content:this})},n.BitmapFontLoader=function(t,e){n.EventTarget.call(this),this.url=t,this.crossorigin=e,this.baseUrl=t.replace(/[^\/]*$/,""),this.texture=null},n.BitmapFontLoader.prototype.constructor=n.BitmapFontLoader,n.BitmapFontLoader.prototype.load=function(){this.ajaxRequest=new XMLHttpRequest;var t=this;this.ajaxRequest.onreadystatechange=function(){t.onXMLLoaded()},this.ajaxRequest.open("GET",this.url,!0),this.ajaxRequest.overrideMimeType&&this.ajaxRequest.overrideMimeType("application/xml"),this.ajaxRequest.send(null)},n.BitmapFontLoader.prototype.onXMLLoaded=function(){if(4==this.ajaxRequest.readyState&&(200==this.ajaxRequest.status||-1==window.location.href.indexOf("http"))){var t=this.baseUrl+this.ajaxRequest.responseXML.getElementsByTagName("page")[0].attributes.getNamedItem("file").nodeValue,e=new n.ImageLoader(t,this.crossorigin);this.texture=e.texture.baseTexture;var i={},r=this.ajaxRequest.responseXML.getElementsByTagName("info")[0],s=this.ajaxRequest.responseXML.getElementsByTagName("common")[0];i.font=r.attributes.getNamedItem("face").nodeValue,i.size=parseInt(r.attributes.getNamedItem("size").nodeValue,10),i.lineHeight=parseInt(s.attributes.getNamedItem("lineHeight").nodeValue,10),i.chars={};for(var o=this.ajaxRequest.responseXML.getElementsByTagName("char"),a=0;o.length>a;a++){var h=parseInt(o[a].attributes.getNamedItem("id").nodeValue,10),l={x:parseInt(o[a].attributes.getNamedItem("x").nodeValue,10),y:parseInt(o[a].attributes.getNamedItem("y").nodeValue,10),width:parseInt(o[a].attributes.getNamedItem("width").nodeValue,10),height:parseInt(o[a].attributes.getNamedItem("height").nodeValue,10)};n.TextureCache[h]=new n.Texture(this.texture,l),i.chars[h]={xOffset:parseInt(o[a].attributes.getNamedItem("xoffset").nodeValue,10),yOffset:parseInt(o[a].attributes.getNamedItem("yoffset").nodeValue,10),xAdvance:parseInt(o[a].attributes.getNamedItem("xadvance").nodeValue,10),kerning:{},texture:new n.Texture(this.texture,l)}}var u=this.ajaxRequest.responseXML.getElementsByTagName("kerning");for(a=0;u.length>a;a++){var c=parseInt(u[a].attributes.getNamedItem("first").nodeValue,10),p=parseInt(u[a].attributes.getNamedItem("second").nodeValue,10),d=parseInt(u[a].attributes.getNamedItem("amount").nodeValue,10);i.chars[p].kerning[c]=d}n.BitmapText.fonts[i.font]=i;var f=this;e.addEventListener("loaded",function(){f.onLoaded()}),e.load()}},n.BitmapFontLoader.prototype.onLoaded=function(){this.dispatchEvent({type:"loaded",content:this})},n.SpineLoader=function(t,e){n.EventTarget.call(this),this.url=t,this.crossorigin=e,this.loaded=!1},n.SpineLoader.prototype.constructor=n.SpineLoader,n.SpineLoader.prototype.load=function(){var t=this,e=new n.JsonLoader(this.url,this.crossorigin);e.addEventListener("loaded",function(e){t.json=e.content.json,t.onJSONLoaded()}),e.load()},n.SpineLoader.prototype.onJSONLoaded=function(){var t=new u.SkeletonJson,e=t.readSkeletonData(this.json);n.AnimCache[this.url]=e,this.onLoaded()},n.SpineLoader.prototype.onLoaded=function(){this.loaded=!0,this.dispatchEvent({type:"loaded",content:this})},"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=n),exports.PIXI=n):i.PIXI=n}).call(this); \ No newline at end of file +!function(){function c(){return f.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,f.Matrix}function d(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}function d(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]}var e=this,f=f||{};f.Point=function(a,b){this.x=a||0,this.y=b||0},f.Point.prototype.clone=function(){return new f.Point(this.x,this.y)},f.Point.prototype.constructor=f.Point,f.Rectangle=function(a,b,c,d){this.x=a||0,this.y=b||0,this.width=c||0,this.height=d||0},f.Rectangle.prototype.clone=function(){return new f.Rectangle(this.x,this.y,this.width,this.height)},f.Rectangle.prototype.contains=function(a,b){if(this.width<=0||this.height<=0)return!1;var c=this.x;if(a>=c&&a<=c+this.width){var d=this.y;if(b>=d&&b<=d+this.height)return!0}return!1},f.Rectangle.prototype.constructor=f.Rectangle,f.Polygon=function(a){if(a instanceof Array||(a=Array.prototype.slice.call(arguments)),"number"==typeof a[0]){for(var b=[],c=0,d=a.length;d>c;c+=2)b.push(new f.Point(a[c],a[c+1]));a=b}this.points=a},f.Polygon.prototype.clone=function(){for(var a=[],b=0;bb!=i>b&&(h-f)*(b-g)/(i-g)+f>a;j&&(c=!c)}return c},f.Polygon.prototype.constructor=f.Polygon,f.Circle=function(a,b,c){this.x=a||0,this.y=b||0,this.radius=c||0},f.Circle.prototype.clone=function(){return new f.Circle(this.x,this.y,this.radius)},f.Circle.prototype.contains=function(a,b){if(this.radius<=0)return!1;var c=this.x-a,d=this.y-b,e=this.radius*this.radius;return c*=c,d*=d,e>=c+d},f.Circle.prototype.constructor=f.Circle,f.Ellipse=function(a,b,c,d){this.x=a||0,this.y=b||0,this.width=c||0,this.height=d||0},f.Ellipse.prototype.clone=function(){return new f.Ellipse(this.x,this.y,this.width,this.height)},f.Ellipse.prototype.contains=function(a,b){if(this.width<=0||this.height<=0)return!1;var c=(a-this.x)/this.width-.5,d=(b-this.y)/this.height-.5;return c*=c,d*=d,.25>c+d},f.Ellipse.getBounds=function(){return new f.Rectangle(this.x,this.y,this.width,this.height)},f.Ellipse.prototype.constructor=f.Ellipse,c(),f.mat3={},f.mat3.create=function(){var a=new f.Matrix(9);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},f.mat3.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},f.mat4={},f.mat4.create=function(){var a=new f.Matrix(16);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},f.mat3.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],f=a[2],g=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],m=b[0],n=b[1],o=b[2],p=b[3],q=b[4],r=b[5],s=b[6],t=b[7],u=b[8];return c[0]=m*d+n*g+o*j,c[1]=m*e+n*h+o*k,c[2]=m*f+n*i+o*l,c[3]=p*d+q*g+r*j,c[4]=p*e+q*h+r*k,c[5]=p*f+q*i+r*l,c[6]=s*d+t*g+u*j,c[7]=s*e+t*h+u*k,c[8]=s*f+t*i+u*l,c},f.mat3.clone=function(a){var b=new f.Matrix(9);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b},f.mat3.transpose=function(a,b){if(!b||a===b){var c=a[1],d=a[2],e=a[5];return a[1]=a[3],a[2]=a[6],a[3]=c,a[5]=a[7],a[6]=d,a[7]=e,a}return b[0]=a[0],b[1]=a[3],b[2]=a[6],b[3]=a[1],b[4]=a[4],b[5]=a[7],b[6]=a[2],b[7]=a[5],b[8]=a[8],b},f.mat3.toMat4=function(a,b){return b||(b=f.mat4.create()),b[15]=1,b[14]=0,b[13]=0,b[12]=0,b[11]=0,b[10]=a[8],b[9]=a[7],b[8]=a[6],b[7]=0,b[6]=a[5],b[5]=a[4],b[4]=a[3],b[3]=0,b[2]=a[2],b[1]=a[1],b[0]=a[0],b},f.mat4.create=function(){var a=new f.Matrix(16);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},f.mat4.transpose=function(a,b){if(!b||a===b){var c=a[1],d=a[2],e=a[3],f=a[6],g=a[7],h=a[11];return a[1]=a[4],a[2]=a[8],a[3]=a[12],a[4]=c,a[6]=a[9],a[7]=a[13],a[8]=d,a[9]=f,a[11]=a[14],a[12]=e,a[13]=g,a[14]=h,a}return b[0]=a[0],b[1]=a[4],b[2]=a[8],b[3]=a[12],b[4]=a[1],b[5]=a[5],b[6]=a[9],b[7]=a[13],b[8]=a[2],b[9]=a[6],b[10]=a[10],b[11]=a[14],b[12]=a[3],b[13]=a[7],b[14]=a[11],b[15]=a[15],b},f.mat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],f=a[2],g=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],m=a[9],n=a[10],o=a[11],p=a[12],q=a[13],r=a[14],s=a[15],t=b[0],u=b[1],v=b[2],w=b[3];return c[0]=t*d+u*h+v*l+w*p,c[1]=t*e+u*i+v*m+w*q,c[2]=t*f+u*j+v*n+w*r,c[3]=t*g+u*k+v*o+w*s,t=b[4],u=b[5],v=b[6],w=b[7],c[4]=t*d+u*h+v*l+w*p,c[5]=t*e+u*i+v*m+w*q,c[6]=t*f+u*j+v*n+w*r,c[7]=t*g+u*k+v*o+w*s,t=b[8],u=b[9],v=b[10],w=b[11],c[8]=t*d+u*h+v*l+w*p,c[9]=t*e+u*i+v*m+w*q,c[10]=t*f+u*j+v*n+w*r,c[11]=t*g+u*k+v*o+w*s,t=b[12],u=b[13],v=b[14],w=b[15],c[12]=t*d+u*h+v*l+w*p,c[13]=t*e+u*i+v*m+w*q,c[14]=t*f+u*j+v*n+w*r,c[15]=t*g+u*k+v*o+w*s,c},f.DisplayObject=function(){this.last=this,this.first=this,this.position=new f.Point,this.scale=new f.Point(1,1),this.pivot=new f.Point(0,0),this.rotation=0,this.alpha=1,this.visible=!0,this.hitArea=null,this.buttonMode=!1,this.renderable=!1,this.parent=null,this.stage=null,this.worldAlpha=1,this._interactive=!1,this.worldTransform=f.mat3.create(),this.localTransform=f.mat3.create(),this.color=[],this.dynamic=!0,this._sr=0,this._cr=1},f.DisplayObject.prototype.constructor=f.DisplayObject,f.DisplayObject.prototype.setInteractive=function(a){this.interactive=a},Object.defineProperty(f.DisplayObject.prototype,"interactive",{get:function(){return this._interactive},set:function(a){this._interactive=a,this.stage&&(this.stage.dirty=!0)}}),Object.defineProperty(f.DisplayObject.prototype,"mask",{get:function(){return this._mask},set:function(a){a?this._mask?(a.start=this._mask.start,a.end=this._mask.end):(this.addFilter(a),a.renderable=!1):(this.removeFilter(this._mask),this._mask.renderable=!0),this._mask=a}}),Object.defineProperty(f.DisplayObject.prototype,"filters",{get:function(){return this._filters},set:function(a){a?(this._filters&&this.removeFilter(this._filters),this.addFilter(a)):this._filters&&this.removeFilter(this._filters),this._filters=a}}),f.DisplayObject.prototype.addFilter=function(a){var b=new f.FilterBlock,c=new f.FilterBlock;a.start=b,a.end=c,b.data=a,c.data=a,b.first=b.last=this,c.first=c.last=this,b.open=!0;var d,e,g=b,h=b;e=this.first._iPrev,e?(d=e._iNext,g._iPrev=e,e._iNext=g):d=this,d&&(d._iPrev=h,h._iNext=d);var g=c,h=c,d=null,e=null;e=this.last,d=e._iNext,d&&(d._iPrev=h,h._iNext=d),g._iPrev=e,e._iNext=g;for(var i=this,j=this.last;i;)i.last==j&&(i.last=c),i=i.parent;this.first=b,this.__renderGroup&&this.__renderGroup.addFilterBlocks(b,c)},f.DisplayObject.prototype.removeFilter=function(a){console.log("YUOIO");var b=a.start,c=b._iNext,d=b._iPrev;c&&(c._iPrev=d),d&&(d._iNext=c),this.first=b._iNext;var e=a.end,c=e._iNext,d=e._iPrev;c&&(c._iPrev=d),d._iNext=c;for(var f=e._iPrev,g=this;g.last==e&&(g.last=f,g=g.parent););this.__renderGroup&&this.__renderGroup.removeFilterBlocks(b,e)},f.DisplayObject.prototype.updateTransform=function(){this.rotation!==this.rotationCache&&(this.rotationCache=this.rotation,this._sr=Math.sin(this.rotation),this._cr=Math.cos(this.rotation));var a=this.localTransform,b=this.parent.worldTransform,c=this.worldTransform;a[0]=this._cr*this.scale.x,a[1]=-this._sr*this.scale.y,a[3]=this._sr*this.scale.x,a[4]=this._cr*this.scale.y;var d=this.pivot.x,e=this.pivot.y,g=a[0],h=a[1],i=this.position.x-a[0]*d-e*a[1],j=a[3],k=a[4],l=this.position.y-a[4]*e-d*a[3],m=b[0],n=b[1],o=b[2],p=b[3],q=b[4],r=b[5];a[2]=i,a[5]=l,c[0]=m*g+n*j,c[1]=m*h+n*k,c[2]=m*i+n*l+o,c[3]=p*g+q*j,c[4]=p*h+q*k,c[5]=p*i+q*l+r,this.worldAlpha=this.alpha*this.parent.worldAlpha,this.vcount=f.visibleCount},f.visibleCount=0,f.DisplayObjectContainer=function(){f.DisplayObject.call(this),this.children=[]},f.DisplayObjectContainer.prototype=Object.create(f.DisplayObject.prototype),f.DisplayObjectContainer.prototype.constructor=f.DisplayObjectContainer,f.DisplayObjectContainer.prototype.addChild=function(a){if(void 0!=a.parent&&a.parent.removeChild(a),a.parent=this,this.children.push(a),this.stage){var b=a;do b.interactive&&(this.stage.dirty=!0),b.stage=this.stage,b=b._iNext;while(b)}var c,d,e=a.first,f=a.last;d=this._filters?this.last._iPrev:this.last,c=d._iNext;for(var g=this,h=d;g;)g.last==h&&(g.last=a.last),g=g.parent;c&&(c._iPrev=f,f._iNext=c),e._iPrev=d,d._iNext=e,this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.addChildAt=function(a,b){if(!(b>=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);if(void 0!=a.parent&&a.parent.removeChild(a),a.parent=this,this.stage){var c=a;do c.interactive&&(this.stage.dirty=!0),c.stage=this.stage,c=c._iNext;while(c)}var d,e,f=a.first,g=a.last;if(b==this.children.length){e=this.last;for(var h=this,i=this.last;h;)h.last==i&&(h.last=a.last),h=h.parent}else e=0==b?this:this.children[b-1].last;d=e._iNext,d&&(d._iPrev=g,g._iNext=d),f._iPrev=e,e._iNext=f,this.children.splice(b,0,a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},f.DisplayObjectContainer.prototype.swapChildren=function(){},f.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&aa;a++)this.children[a].updateTransform()}},f.blendModes={},f.blendModes.NORMAL=0,f.blendModes.SCREEN=1,f.Sprite=function(a){f.DisplayObjectContainer.call(this),this.anchor=new f.Point,this.texture=a,this.blendMode=f.blendModes.NORMAL,this._width=0,this._height=0,a.baseTexture.hasLoaded?this.updateFrame=!0:(this.onTextureUpdateBind=this.onTextureUpdate.bind(this),this.texture.addEventListener("update",this.onTextureUpdateBind)),this.renderable=!0},f.Sprite.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Sprite.prototype.constructor=f.Sprite,Object.defineProperty(f.Sprite.prototype,"width",{get:function(){return this.scale.x*this.texture.frame.width},set:function(a){this.scale.x=a/this.texture.frame.width,this._width=a}}),Object.defineProperty(f.Sprite.prototype,"height",{get:function(){return this.scale.y*this.texture.frame.height},set:function(a){this.scale.y=a/this.texture.frame.height,this._height=a}}),f.Sprite.prototype.setTexture=function(a){this.texture.baseTexture!=a.baseTexture?(this.textureChange=!0,this.texture=a,this.__renderGroup&&this.__renderGroup.updateTexture(this)):this.texture=a,this.updateFrame=!0},f.Sprite.prototype.onTextureUpdate=function(){this._width&&(this.scale.x=this._width/this.texture.frame.width),this._height&&(this.scale.y=this._height/this.texture.frame.height),this.updateFrame=!0},f.Sprite.fromFrame=function(a){var b=f.TextureCache[a];if(!b)throw new Error("The frameId '"+a+"' does not exist in the texture cache"+this);return new f.Sprite(b)},f.Sprite.fromImage=function(a){var b=f.Texture.fromImage(a);return new f.Sprite(b)},f.MovieClip=function(a){f.Sprite.call(this,a[0]),this.textures=a,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.currentFrame=0,this.playing=!1},f.MovieClip.prototype=Object.create(f.Sprite.prototype),f.MovieClip.prototype.constructor=f.MovieClip,Object.defineProperty(f.MovieClip.prototype,"totalFrames",{get:function(){return this.textures.length}}),f.MovieClip.prototype.stop=function(){this.playing=!1},f.MovieClip.prototype.play=function(){this.playing=!0},f.MovieClip.prototype.gotoAndStop=function(a){this.playing=!1,this.currentFrame=a;var b=0|this.currentFrame+.5;this.setTexture(this.textures[b%this.textures.length])},f.MovieClip.prototype.gotoAndPlay=function(a){this.currentFrame=a,this.playing=!0},f.MovieClip.prototype.updateTransform=function(){if(f.Sprite.prototype.updateTransform.call(this),this.playing){this.currentFrame+=this.animationSpeed;var a=0|this.currentFrame+.5;this.loop||a=this.textures.length&&(this.gotoAndStop(this.textures.length-1),this.onComplete&&this.onComplete())}},f.FilterBlock=function(){this.visible=!0,this.renderable=!0},f.ColorMatrixFilter=function(){this.uniforms={matrix:{type:"mat4",value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]}},this.fragmentSrc=["precision mediump float;","varying vec2 vTextureCoord;","varying float vColor;","uniform float invert;","uniform mat4 matrix;","uniform sampler2D uSampler;","void main(void) {","gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;","gl_FragColor = gl_FragColor * vColor;","}"]},Object.defineProperty(f.ColorMatrixFilter.prototype,"matrix",{get:function(){return this.uniforms.matrix.value},set:function(a){this.uniforms.matrix.value=a}}),f.Text=function(a,b){this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),f.Sprite.call(this,f.Texture.fromCanvas(this.canvas)),this.setText(a),this.setStyle(b),this.updateText(),this.dirty=!1},f.Text.prototype=Object.create(f.Sprite.prototype),f.Text.prototype.constructor=f.Text,f.Text.prototype.setStyle=function(a){a=a||{},a.font=a.font||"bold 20pt Arial",a.fill=a.fill||"black",a.align=a.align||"left",a.stroke=a.stroke||"black",a.strokeThickness=a.strokeThickness||0,a.wordWrap=a.wordWrap||!1,a.wordWrapWidth=a.wordWrapWidth||100,this.style=a,this.dirty=!0},f.Text.prototype.setText=function(a){this.text=a.toString()||" ",this.dirty=!0},f.Text.prototype.updateText=function(){this.context.font=this.style.font;var a=this.text;this.style.wordWrap&&(a=this.wordWrap(this.text));for(var b=a.split(/(?:\r\n|\r|\n)/),c=[],d=0,e=0;ee?f:arguments.callee(a,b,f,d,e):arguments.callee(a,b,c,f,e)},c=function(a,c,d){if(a.measureText(c).width<=d||c.length<1)return c;var e=b(a,c,0,c.length,d);return c.substring(0,e)+"\n"+arguments.callee(a,c.substring(e),d)},d="",e=a.split("\n"),f=0;f=2?parseInt(b[b.length-2],10):f.BitmapText.fonts[this.fontName].size,this.dirty=!0},f.BitmapText.prototype.updateText=function(){for(var a=f.BitmapText.fonts[this.fontName],b=new f.Point,c=null,d=[],e=0,g=[],h=0,i=this.fontSize/a.size,j=0;j=j;j++){var n=0;"right"==this.style.align?n=e-g[j]:"center"==this.style.align&&(n=(e-g[j])/2),m.push(n)}for(j=0;j0;)this.removeChild(this.getChildAt(0));this.updateText(),this.dirty=!1}f.DisplayObjectContainer.prototype.updateTransform.call(this)},f.BitmapText.fonts={},f.InteractionManager=function(a){this.stage=a,this.mouse=new f.InteractionData,this.touchs={},this.tempPoint=new f.Point,this.mouseoverEnabled=!0,this.pool=[],this.interactiveItems=[],this.interactionDOMElement=null,this.onMouseMove=this.onMouseMove.bind(this),this.onMouseDown=this.onMouseDown.bind(this),this.onMouseOut=this.onMouseOut.bind(this),this.onMouseUp=this.onMouseUp.bind(this),this.onTouchStart=this.onTouchStart.bind(this),this.onTouchEnd=this.onTouchEnd.bind(this),this.onTouchMove=this.onTouchMove.bind(this),this.last=0},f.InteractionManager.prototype.constructor=f.InteractionManager,f.InteractionManager.prototype.collectInteractiveSprite=function(a,b){for(var c=a.children,d=c.length,e=d-1;e>=0;e--){var f=c[e];f.interactive?(b.interactiveChildren=!0,this.interactiveItems.push(f),f.children.length>0&&this.collectInteractiveSprite(f,f)):(f.__iParent=null,f.children.length>0&&this.collectInteractiveSprite(f,b))}},f.InteractionManager.prototype.setTarget=function(a){this.target=a,null===this.interactionDOMElement&&this.setTargetDomElement(a.view),document.body.addEventListener("mouseup",this.onMouseUp,!0)},f.InteractionManager.prototype.setTargetDomElement=function(a){null!==this.interactionDOMElement&&(this.interactionDOMElement.style["-ms-content-zooming"]="",this.interactionDOMElement.style["-ms-touch-action"]="",this.interactionDOMElement.removeEventListener("mousemove",this.onMouseMove,!0),this.interactionDOMElement.removeEventListener("mousedown",this.onMouseDown,!0),this.interactionDOMElement.removeEventListener("mouseout",this.onMouseOut,!0),this.interactionDOMElement.removeEventListener("touchstart",this.onTouchStart,!0),this.interactionDOMElement.removeEventListener("touchend",this.onTouchEnd,!0),this.interactionDOMElement.removeEventListener("touchmove",this.onTouchMove,!0)),window.navigator.msPointerEnabled&&(a.style["-ms-content-zooming"]="none",a.style["-ms-touch-action"]="none"),this.interactionDOMElement=a,a.addEventListener("mousemove",this.onMouseMove,!0),a.addEventListener("mousedown",this.onMouseDown,!0),a.addEventListener("mouseout",this.onMouseOut,!0),a.addEventListener("touchstart",this.onTouchStart,!0),a.addEventListener("touchend",this.onTouchEnd,!0),a.addEventListener("touchmove",this.onTouchMove,!0)},f.InteractionManager.prototype.update=function(){if(this.target){var a=Date.now(),b=a-this.last;if(b=30*b/1e3,!(1>b)){if(this.last=a,this.dirty){this.dirty=!1;for(var c=this.interactiveItems.length,d=0;c>d;d++)this.interactiveItems[d].interactiveChildren=!1;this.interactiveItems=[],this.stage.interactive&&this.interactiveItems.push(this.stage),this.collectInteractiveSprite(this.stage,this.stage)}var e=this.interactiveItems.length;this.interactionDOMElement.style.cursor="default";for(var d=0;e>d;d++){var f=this.interactiveItems[d];(f.mouseover||f.mouseout||f.buttonMode)&&(f.__hit=this.hitTest(f,this.mouse),this.mouse.target=f,f.__hit?(f.buttonMode&&(this.interactionDOMElement.style.cursor="pointer"),f.__isOver||(f.mouseover&&f.mouseover(this.mouse),f.__isOver=!0)):f.__isOver&&(f.mouseout&&f.mouseout(this.mouse),f.__isOver=!1))}}}},f.InteractionManager.prototype.onMouseMove=function(a){this.mouse.originalEvent=a||window.event;var b=this.interactionDOMElement.getBoundingClientRect();this.mouse.global.x=(a.clientX-b.left)*(this.target.width/b.width),this.mouse.global.y=(a.clientY-b.top)*(this.target.height/b.height);var c=this.interactiveItems.length;this.mouse.global;for(var d=0;c>d;d++){var e=this.interactiveItems[d];e.mousemove&&e.mousemove(this.mouse)}},f.InteractionManager.prototype.onMouseDown=function(a){this.mouse.originalEvent=a||window.event;var b=this.interactiveItems.length;this.mouse.global,this.stage;for(var c=0;b>c;c++){var d=this.interactiveItems[c];if((d.mousedown||d.click)&&(d.__mouseIsDown=!0,d.__hit=this.hitTest(d,this.mouse),d.__hit&&(d.mousedown&&d.mousedown(this.mouse),d.__isDown=!0,!d.interactiveChildren)))break}},f.InteractionManager.prototype.onMouseOut=function(){var a=this.interactiveItems.length;this.interactionDOMElement.style.cursor="default";for(var b=0;a>b;b++){var c=this.interactiveItems[b];c.__isOver&&(this.mouse.target=c,c.mouseout&&c.mouseout(this.mouse),c.__isOver=!1)}},f.InteractionManager.prototype.onMouseUp=function(a){this.mouse.originalEvent=a||window.event,this.mouse.global;for(var b=this.interactiveItems.length,c=!1,d=0;b>d;d++){var e=this.interactiveItems[d];(e.mouseup||e.mouseupoutside||e.click)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit&&!c?(e.mouseup&&e.mouseup(this.mouse),e.__isDown&&e.click&&e.click(this.mouse),e.interactiveChildren||(c=!0)):e.__isDown&&e.mouseupoutside&&e.mouseupoutside(this.mouse),e.__isDown=!1)}},f.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(a.vcount!==f.visibleCount)return!1;var d=a instanceof f.Sprite,e=a.worldTransform,g=e[0],h=e[1],i=e[2],j=e[3],k=e[4],l=e[5],m=1/(g*k+h*-j),n=k*m*c.x+-h*m*c.y+(l*h-i*k)*m,o=g*m*c.y+-j*m*c.x+(-l*g+i*j)*m;if(b.target=a,a.hitArea&&a.hitArea.contains)return a.hitArea.contains(n,o)?(b.target=a,!0):!1;if(d){var p,q=a.texture.frame.width,r=a.texture.frame.height,s=-q*a.anchor.x;if(n>s&&s+q>n&&(p=-r*a.anchor.y,o>p&&p+r>o))return b.target=a,!0}for(var t=a.children.length,u=0;t>u;u++){var v=a.children[u],w=this.hitTest(v,b);if(w)return b.target=a,!0}return!1},f.InteractionManager.prototype.onTouchMove=function(a){for(var b=this.interactionDOMElement.getBoundingClientRect(),c=a.changedTouches,d=0;dd;d++){var h=this.interactiveItems[d];h.touchmove&&h.touchmove(f)}},f.InteractionManager.prototype.onTouchStart=function(a){for(var b=this.interactionDOMElement.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i];if((j.touchstart||j.tap)&&(j.__hit=this.hitTest(j,g),j.__hit&&(j.touchstart&&j.touchstart(g),j.__isDown=!0,j.__touchData=g,!j.interactiveChildren)))break}}},f.InteractionManager.prototype.onTouchEnd=function(a){for(var b=this.interactionDOMElement.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k==f&&(f.originalEvent=a||window.event,(j.touchend||j.tap)&&(j.__hit&&!g?(j.touchend&&j.touchend(f),j.__isDown&&j.tap&&j.tap(f),j.interactiveChildren||(g=!0)):j.__isDown&&j.touchendoutside&&j.touchendoutside(f),j.__isDown=!1),j.__touchData=null)}this.pool.push(f),this.touchs[e.identifier]=null}},f.InteractionData=function(){this.global=new f.Point,this.local=new f.Point,this.target,this.originalEvent},f.InteractionData.prototype.getLocalPosition=function(a){var b=a.worldTransform,c=this.global,d=b[0],e=b[1],g=b[2],h=b[3],i=b[4],j=b[5],k=1/(d*i+e*-h);return new f.Point(i*k*c.x+-e*k*c.y+(j*e-g*i)*k,d*k*c.y+-h*k*c.x+(-j*d+g*h)*k)},f.InteractionData.prototype.constructor=f.InteractionData,f.Stage=function(a){f.DisplayObjectContainer.call(this),this.worldTransform=f.mat3.create(),this.interactive=!0,this.interactionManager=new f.InteractionManager(this),this.dirty=!0,this.__childrenAdded=[],this.__childrenRemoved=[],this.stage=this,this.stage.hitArea=new f.Rectangle(0,0,1e5,1e5),this.setBackgroundColor(a),this.worldVisible=!0},f.Stage.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Stage.prototype.constructor=f.Stage,f.Stage.prototype.setInteractionDelegate=function(a){this.interactionManager.setTargetDomElement(a)},f.Stage.prototype.updateTransform=function(){this.worldAlpha=1,this.vcount=f.visibleCount;for(var a=0,b=this.children.length;b>a;a++)this.children[a].updateTransform();this.dirty&&(this.dirty=!1,this.interactionManager.dirty=!0),this.interactive&&this.interactionManager.update()},f.Stage.prototype.setBackgroundColor=function(a){this.backgroundColor=a||0,this.backgroundColorSplit=d(this.backgroundColor);var b=this.backgroundColor.toString(16);b="000000".substr(0,6-b.length)+b,this.backgroundColorString="#"+b},f.Stage.prototype.getMousePosition=function(){return this.interactionManager.mouse.global};for(var h=0,i=["ms","moz","webkit","o"],j=0;j>>>>>>>>"),console.log("_");var b=0,c=a.first;for(console.log(c);c._iNext;)if(b++,c=c._iNext,console.log(c),b>100){console.log("BREAK");break}},f.EventTarget=function(){var a={};this.addEventListener=this.on=function(b,c){void 0===a[b]&&(a[b]=[]),-1===a[b].indexOf(c)&&a[b].push(c)},this.dispatchEvent=this.emit=function(b){if(a[b.type]&&a[b.type].length)for(var c=0,d=a[b.type].length;d>c;c++)a[b.type][c](b)},this.removeEventListener=this.off=function(b,c){var d=a[b].indexOf(c);-1!==d&&a[b].splice(d,1)}},f.autoDetectRenderer=function(a,b,c,d,e){a||(a=800),b||(b=600);var g=function(){try{return!!window.WebGLRenderingContext&&!!document.createElement("canvas").getContext("experimental-webgl")}catch(a){return!1}}();return g?new f.WebGLRenderer(a,b,c,d,e):new f.CanvasRenderer(a,b,c,d)},f.PolyK={},f.PolyK.Triangulate=function(a){var b=!0,c=a.length>>1;if(3>c)return[];for(var d=[],e=[],g=0;c>g;g++)e.push(g);for(var g=0,h=c;h>3;){var i=e[(g+0)%h],j=e[(g+1)%h],k=e[(g+2)%h],l=a[2*i],m=a[2*i+1],n=a[2*j],o=a[2*j+1],p=a[2*k],q=a[2*k+1],r=!1;if(f.PolyK._convex(l,m,n,o,p,q,b)){r=!0;for(var s=0;h>s;s++){var t=e[s];if(t!=i&&t!=j&&t!=k&&f.PolyK._PointInTriangle(a[2*t],a[2*t+1],l,m,n,o,p,q)){r=!1;break}}}if(r)d.push(i,j,k),e.splice((g+1)%h,1),h--,g=0;else if(g++>3*h){if(!b)return console.log("PIXI Warning: shape too complex to fill"),[];var d=[];e=[];for(var g=0;c>g;g++)e.push(g);g=0,h=c,b=!1}}return d.push(e[0],e[1],e[2]),d},f.PolyK._PointInTriangle=function(a,b,c,d,e,f,g,h){var i=g-c,j=h-d,k=e-c,l=f-d,m=a-c,n=b-d,o=i*i+j*j,p=i*k+j*l,q=i*m+j*n,r=k*k+l*l,s=k*m+l*n,t=1/(o*r-p*p),u=(r*q-p*s)*t,v=(o*s-p*q)*t;return u>=0&&v>=0&&1>u+v},f.PolyK._convex=function(a,b,c,d,e,f,g){return(b-d)*(e-c)+(c-a)*(f-d)>=0==g},f.shaderFragmentSrc=["precision mediump float;","varying vec2 vTextureCoord;","varying float vColor;","uniform sampler2D uSampler;","void main(void) {","gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));","gl_FragColor = gl_FragColor * vColor;","}"],f.shaderVertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aTextureCoord;","attribute float aColor;","uniform vec2 projectionVector;","varying vec2 vTextureCoord;","varying float vColor;","void main(void) {","gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);","vTextureCoord = aTextureCoord;","vColor = aColor;","}"],f.stripShaderFragmentSrc=["precision mediump float;","varying vec2 vTextureCoord;","varying float vColor;","uniform float alpha;","uniform sampler2D uSampler;","void main(void) {","gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));","gl_FragColor = gl_FragColor * alpha;","}"],f.stripShaderVertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aTextureCoord;","attribute float aColor;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","varying vec2 vTextureCoord;","varying float vColor;","void main(void) {","vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);","gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);","vTextureCoord = aTextureCoord;","vColor = aColor;","}"],f.primitiveShaderFragmentSrc=["precision mediump float;","varying vec4 vColor;","void main(void) {","gl_FragColor = vColor;","}"],f.primitiveShaderVertexSrc=["attribute vec2 aVertexPosition;","attribute vec4 aColor;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","uniform float alpha;","varying vec4 vColor;","void main(void) {","vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);","gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);","vColor = aColor * alpha;","}"],f.shaderStack=[],f.initPrimitiveShader=function(){var a=f.gl,b=f.compileProgram(f.primitiveShaderVertexSrc,f.primitiveShaderFragmentSrc); +a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.translationMatrix=a.getUniformLocation(b,"translationMatrix"),b.alpha=a.getUniformLocation(b,"alpha"),f.primitiveProgram=b},f.initDefaultShader=function(){f.defaultShader=new f.PixiShader,f.defaultShader.init(),f.activateShader(f.defaultShader)},f.initDefaultStripShader=function(){var a=this.gl,b=f.compileProgram(f.stripShaderVertexSrc,f.stripShaderFragmentSrc);a.useProgram(b),b.vertexPositionAttribute=a.getAttribLocation(b,"aVertexPosition"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.textureCoordAttribute=a.getAttribLocation(b,"aTextureCoord"),b.translationMatrix=a.getUniformLocation(b,"translationMatrix"),b.alpha=a.getUniformLocation(b,"alpha"),b.colorAttribute=a.getAttribLocation(b,"aColor"),b.projectionVector=a.getUniformLocation(b,"projectionVector"),b.samplerUniform=a.getUniformLocation(b,"uSampler"),f.stripShaderProgram=b},f.CompileVertexShader=function(a,b){return f._CompileShader(a,b,a.VERTEX_SHADER)},f.CompileFragmentShader=function(a,b){return f._CompileShader(a,b,a.FRAGMENT_SHADER)},f._CompileShader=function(a,b,c){var d=b.join("\n"),e=a.createShader(c);return a.shaderSource(e,d),a.compileShader(e),a.getShaderParameter(e,a.COMPILE_STATUS)?e:(alert(a.getShaderInfoLog(e)),null)},f.compileProgram=function(a,b){var c=f.gl,d=f.CompileFragmentShader(c,b),e=f.CompileVertexShader(c,a),g=c.createProgram();return c.attachShader(g,e),c.attachShader(g,d),c.linkProgram(g),c.getProgramParameter(g,c.LINK_STATUS)||alert("Could not initialise shaders"),g},f.activateShader=function(a){f.shaderStack.push(a);var b=f.gl,c=a.program;b.useProgram(c),b.enableVertexAttribArray(c.vertexPositionAttribute),b.enableVertexAttribArray(c.colorAttribute),b.enableVertexAttribArray(c.textureCoordAttribute),a.syncUniforms(),f.currentShader=c},f.popShader=function(){var a=f.gl;f.shaderStack.pop();var b=f.shaderStack[f.shaderStack.length-1].program;a.useProgram(b),f.currentShader=b},f.activatePrimitiveShader=function(){var a=f.gl;a.useProgram(f.primitiveProgram),a.disableVertexAttribArray(f.currentShader.textureCoordAttribute)},f.deactivatePrimitiveShader=function(){var a=f.gl;a.useProgram(f.currentShader),a.enableVertexAttribArray(f.currentShader.textureCoordAttribute)},f.PixiShader=function(){this.program,this.fragmentSrc=["precision lowp float;","varying vec2 vTextureCoord;","varying float vColor;","uniform sampler2D uSampler;","void main(void) {","gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;","}"]},f.PixiShader.prototype.init=function(){var a=f.compileProgram(this.vertexSrc||f.shaderVertexSrc,this.fragmentSrc),b=f.gl;b.useProgram(a),a.vertexPositionAttribute=b.getAttribLocation(a,"aVertexPosition"),a.colorAttribute=b.getAttribLocation(a,"aColor"),a.textureCoordAttribute=b.getAttribLocation(a,"aTextureCoord"),a.projectionVector=b.getUniformLocation(a,"projectionVector"),a.samplerUniform=b.getUniformLocation(a,"uSampler");for(var c in this.uniforms)a[c]=b.getUniformLocation(a,c);this.program=a},f.PixiShader.prototype.syncUniforms=function(){var a=f.gl;for(var b in this.uniforms){var c=this.uniforms[b].type;"f"==c?a.uniform1f(this.program[b],this.uniforms[b].value):"mat4"==c&&a.uniformMatrix4fv(this.program[b],!1,this.uniforms[b].value)}},f.WebGLGraphics=function(){},f.WebGLGraphics.renderGraphics=function(a,b){var c=f.gl;a._webGL||(a._webGL={points:[],indices:[],lastIndex:0,buffer:c.createBuffer(),indexBuffer:c.createBuffer()}),a.dirty&&(a.dirty=!1,a.clearDirty&&(a.clearDirty=!1,a._webGL.lastIndex=0,a._webGL.points=[],a._webGL.indices=[]),f.WebGLGraphics.updateGraphics(a)),f.activatePrimitiveShader();var d=f.mat3.clone(a.worldTransform);f.mat3.transpose(d),c.blendFunc(c.ONE,c.ONE_MINUS_SRC_ALPHA),c.uniformMatrix3fv(f.primitiveProgram.translationMatrix,!1,d),c.uniform2f(f.primitiveProgram.projectionVector,b.x,b.y),c.uniform1f(f.primitiveProgram.alpha,a.worldAlpha),c.bindBuffer(c.ARRAY_BUFFER,a._webGL.buffer),c.vertexAttribPointer(f.primitiveProgram.vertexPositionAttribute,2,c.FLOAT,!1,24,0),c.vertexAttribPointer(f.primitiveProgram.colorAttribute,4,c.FLOAT,!1,24,8),c.bindBuffer(c.ELEMENT_ARRAY_BUFFER,a._webGL.indexBuffer),c.drawElements(c.TRIANGLE_STRIP,a._webGL.indices.length,c.UNSIGNED_SHORT,0),f.deactivatePrimitiveShader()},f.WebGLGraphics.updateGraphics=function(a){for(var b=a._webGL.lastIndex;b3&&f.WebGLGraphics.buildPoly(c,a._webGL),c.lineWidth>0&&f.WebGLGraphics.buildLine(c,a._webGL)):c.type==f.Graphics.RECT?f.WebGLGraphics.buildRectangle(c,a._webGL):(c.type==f.Graphics.CIRC||c.type==f.Graphics.ELIP)&&f.WebGLGraphics.buildCircle(c,a._webGL)}a._webGL.lastIndex=a.graphicsData.length;var d=f.gl;a._webGL.glPoints=new Float32Array(a._webGL.points),d.bindBuffer(d.ARRAY_BUFFER,a._webGL.buffer),d.bufferData(d.ARRAY_BUFFER,a._webGL.glPoints,d.STATIC_DRAW),a._webGL.glIndicies=new Uint16Array(a._webGL.indices),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,a._webGL.indexBuffer),d.bufferData(d.ELEMENT_ARRAY_BUFFER,a._webGL.glIndicies,d.STATIC_DRAW)},f.WebGLGraphics.buildRectangle=function(a,b){var c=a.points,e=c[0],g=c[1],h=c[2],i=c[3];if(a.fill){var j=d(a.fillColor),k=a.fillAlpha,l=j[0]*k,m=j[1]*k,n=j[2]*k,o=b.points,p=b.indices,q=o.length/6;o.push(e,g),o.push(l,m,n,k),o.push(e+h,g),o.push(l,m,n,k),o.push(e,g+i),o.push(l,m,n,k),o.push(e+h,g+i),o.push(l,m,n,k),p.push(q,q,q+1,q+2,q+3,q+3)}a.lineWidth&&(a.points=[e,g,e+h,g,e+h,g+i,e,g+i,e,g],f.WebGLGraphics.buildLine(a,b))},f.WebGLGraphics.buildCircle=function(a,b){var c=a.points,e=c[0],g=c[1],h=c[2],i=c[3],j=40,k=2*Math.PI/j;if(a.fill){var l=d(a.fillColor),m=a.fillAlpha,n=l[0]*m,o=l[1]*m,p=l[2]*m,q=b.points,r=b.indices,s=q.length/6;r.push(s);for(var t=0;j+1>t;t++)q.push(e,g,n,o,p,m),q.push(e+Math.sin(k*t)*h,g+Math.cos(k*t)*i,n,o,p,m),r.push(s++,s++);r.push(s-1)}if(a.lineWidth){a.points=[];for(var t=0;j+1>t;t++)a.points.push(e+Math.sin(k*t)*h,g+Math.cos(k*t)*i);f.WebGLGraphics.buildLine(a,b)}},f.WebGLGraphics.buildLine=function(a,b){var c=a.points;if(0!=c.length){var e=new f.Point(c[0],c[1]),g=new f.Point(c[c.length-2],c[c.length-1]);if(e.x==g.x&&e.y==g.y){c.pop(),c.pop(),g=new f.Point(c[c.length-2],c[c.length-1]);var h=g.x+.5*(e.x-g.x),i=g.y+.5*(e.y-g.y);c.unshift(h,i),c.push(h,i)}var j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E=b.points,F=b.indices,G=c.length/2,H=c.length,I=E.length/6,J=a.lineWidth/2,K=d(a.lineColor),L=a.lineAlpha,M=K[0]*L,N=K[1]*L,O=K[2]*L;j=c[0],k=c[1],l=c[2],m=c[3],p=-(k-m),q=j-l,D=Math.sqrt(p*p+q*q),p/=D,q/=D,p*=J,q*=J,E.push(j-p,k-q,M,N,O,L),E.push(j+p,k+q,M,N,O,L);for(var P=1;G-1>P;P++)j=c[2*(P-1)],k=c[2*(P-1)+1],l=c[2*P],m=c[2*P+1],n=c[2*(P+1)],o=c[2*(P+1)+1],p=-(k-m),q=j-l,D=Math.sqrt(p*p+q*q),p/=D,q/=D,p*=J,q*=J,r=-(m-o),s=l-n,D=Math.sqrt(r*r+s*s),r/=D,s/=D,r*=J,s*=J,v=-q+k-(-q+m),w=-p+l-(-p+j),x=(-p+j)*(-q+m)-(-p+l)*(-q+k),y=-s+o-(-s+m),z=-r+l-(-r+n),A=(-r+n)*(-s+m)-(-r+l)*(-s+o),B=v*z-y*w,0==B&&(B+=1),px=(w*A-z*x)/B,py=(y*x-v*A)/B,C=(px-l)*(px-l)+(py-m)+(py-m),C>19600?(t=p-r,u=q-s,D=Math.sqrt(t*t+u*u),t/=D,u/=D,t*=J,u*=J,E.push(l-t,m-u),E.push(M,N,O,L),E.push(l+t,m+u),E.push(M,N,O,L),E.push(l-t,m-u),E.push(M,N,O,L),H++):(E.push(px,py),E.push(M,N,O,L),E.push(l-(px-l),m-(py-m)),E.push(M,N,O,L));j=c[2*(G-2)],k=c[2*(G-2)+1],l=c[2*(G-1)],m=c[2*(G-1)+1],p=-(k-m),q=j-l,D=Math.sqrt(p*p+q*q),p/=D,q/=D,p*=J,q*=J,E.push(l-p,m-q),E.push(M,N,O,L),E.push(l+p,m+q),E.push(M,N,O,L),F.push(I);for(var P=0;H>P;P++)F.push(I++);F.push(I-1)}},f.WebGLGraphics.buildPoly=function(a,b){var c=a.points;if(!(c.length<6)){for(var e=b.points,g=b.indices,h=c.length/2,i=d(a.fillColor),j=a.fillAlpha,k=i[0]*j,l=i[1]*j,m=i[2]*j,n=f.PolyK.Triangulate(c),o=e.length/6,p=0;pp;p++)e.push(c[2*p],c[2*p+1],k,l,m,j)}},f._defaultFrame=new f.Rectangle(0,0,1,1),f.gl,f.WebGLRenderer=function(a,b,c,d,e){this.transparent=!!d,this.width=a||800,this.height=b||600,this.view=c||document.createElement("canvas"),this.view.width=this.width,this.view.height=this.height;var g=this;this.view.addEventListener("webglcontextlost",function(a){g.handleContextLost(a)},!1),this.view.addEventListener("webglcontextrestored",function(a){g.handleContextRestored(a)},!1),this.batchs=[];try{f.gl=this.gl=this.view.getContext("experimental-webgl",{alpha:this.transparent,antialias:!!e,premultipliedAlpha:!1,stencil:!0})}catch(h){throw new Error(" This browser does not support webGL. Try using the canvas renderer"+this)}f.initDefaultShader(),f.initPrimitiveShader(),f.initDefaultStripShader();var i=this.gl;f.WebGLRenderer.gl=i,this.batch=new f.WebGLBatch(i),i.disable(i.DEPTH_TEST),i.disable(i.CULL_FACE),i.enable(i.BLEND),i.colorMask(!0,!0,!0,this.transparent),f.projection=new f.Point(400,300),this.resize(this.width,this.height),this.contextLost=!1,f.activateShader(f.defaultShader),this.stageRenderGroup=new f.WebGLRenderGroup(this.gl)},f.WebGLRenderer.prototype.constructor=f.WebGLRenderer,f.WebGLRenderer.getBatch=function(){return 0==f._batchs.length?new f.WebGLBatch(f.WebGLRenderer.gl):f._batchs.pop()},f.WebGLRenderer.returnBatch=function(a){a.clean(),f._batchs.push(a)},f.WebGLRenderer.prototype.render=function(a){if(!this.contextLost){this.__stage!==a&&(this.__stage=a,this.stageRenderGroup.setRenderable(a)),f.WebGLRenderer.updateTextures(),f.visibleCount++,a.updateTransform();var b=this.gl;if(b.colorMask(!0,!0,!0,this.transparent),b.viewport(0,0,this.width,this.height),b.bindFramebuffer(b.FRAMEBUFFER,null),b.clearColor(a.backgroundColorSplit[0],a.backgroundColorSplit[1],a.backgroundColorSplit[2],!this.transparent),b.clear(b.COLOR_BUFFER_BIT),this.stageRenderGroup.backgroundColor=a.backgroundColorSplit,this.stageRenderGroup.render(f.projection),a.interactive&&(a._interactiveEventsAdded||(a._interactiveEventsAdded=!0,a.interactionManager.setTarget(this))),f.Texture.frameUpdates.length>0){for(var c=0;cc;c++){var d=6*c,e=4*c;this.indices[d+0]=e+0,this.indices[d+1]=e+1,this.indices[d+2]=e+2,this.indices[d+3]=e+0,this.indices[d+4]=e+2,this.indices[d+5]=e+3}a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,this.indexBuffer),a.bufferData(a.ELEMENT_ARRAY_BUFFER,this.indices,a.STATIC_DRAW)},f.WebGLBatch.prototype.refresh=function(){this.gl,this.dynamicSize0;)n=n.children[n.children.length-1],n.renderable&&(m=n.last);if(m instanceof f.Sprite){l=m.batch;var k=l.head;if(k==m)g=0;else for(g=1;k.__next!=m;)g++,k=k.__next}else l=m;if(j==l)return j instanceof f.WebGLBatch?j.render(d,g+1):this.renderSpecial(j,b),void 0;e=this.batchs.indexOf(j),h=this.batchs.indexOf(l),j instanceof f.WebGLBatch?j.render(d):this.renderSpecial(j,b);for(var o=e+1;h>o;o++)renderable=this.batchs[o],renderable instanceof f.WebGLBatch?this.batchs[o].render():this.renderSpecial(renderable,b);l instanceof f.WebGLBatch?l.render(0,g+1):this.renderSpecial(l,b)},f.WebGLRenderGroup.prototype.renderSpecial=function(a,b){f.shaderStack.length;var c=a.vcount===f.visibleCount;a instanceof f.TilingSprite?c&&this.renderTilingSprite(a,b):a instanceof f.Strip?c&&this.renderStrip(a,b):a instanceof f.CustomRenderable?c&&a.renderWebGL(this,b):a instanceof f.Graphics?c&&a.renderable&&f.WebGLGraphics.renderGraphics(a,b):a instanceof f.FilterBlock&&this.handleFilterBlock(a,b)},f.WebGLRenderGroup.prototype.handleFilterBlock=function(a,b){var c=f.gl;if(a.open)if(a.data instanceof Array){var d=a.data[0];if(!d.shader){var e=new f.PixiShader;e.fragmentSrc=d.fragmentSrc,e.uniforms=d.uniforms,e.init(),d.shader=e}f.activateShader(d.shader),c.uniform2f(f.currentShader.projectionVector,b.x,b.y)}else c.enable(c.STENCIL_TEST),c.colorMask(!1,!1,!1,!1),c.stencilFunc(c.ALWAYS,1,255),c.stencilOp(c.KEEP,c.KEEP,c.REPLACE),f.WebGLGraphics.renderGraphics(a.data,b),c.colorMask(!0,!0,!0,!0),c.stencilFunc(c.NOTEQUAL,0,255),c.stencilOp(c.KEEP,c.KEEP,c.KEEP);else a.data instanceof Array?(f.popShader(),c.uniform2f(f.currentShader.projectionVector,b.x,b.y)):c.disable(c.STENCIL_TEST)},f.WebGLRenderGroup.prototype.updateTexture=function(a){this.removeObject(a);for(var b=a.first;b!=this.root&&(b=b._iPrev,!b.renderable||!b.__renderGroup););for(var c=a.last;c._iNext&&(c=c._iNext,!c.renderable||!c.__renderGroup););this.insertObject(a,b,c)},f.WebGLRenderGroup.prototype.addFilterBlocks=function(a,b){a.__renderGroup=this,b.__renderGroup=this;for(var c=a;c!=this.root.first&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root.first&&(d=d._iPrev,!d.renderable||!d.__renderGroup););this.insertAfter(b,d)},f.WebGLRenderGroup.prototype.removeFilterBlocks=function(a,b){this.removeObject(a),this.removeObject(b)},f.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a);for(var b=a.first;b!=this.root.first&&(b=b._iPrev,!b.renderable||!b.__renderGroup););for(var c=a.last;c._iNext&&(c=c._iNext,!c.renderable||!c.__renderGroup););var d=a.first,e=a.last._iNext;do d.__renderGroup=this,d.renderable&&(this.insertObject(d,b,c),b=d),d=d._iNext;while(d!=e)},f.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren=function(a){if(a.__renderGroup==this){a.last;do a.__renderGroup=null,a.renderable&&this.removeObject(a),a=a._iNext;while(a)}},f.WebGLRenderGroup.prototype.insertObject=function(a,b,c){var d=b,e=c;if(a instanceof f.Sprite){var g,h;if(d instanceof f.Sprite){if(g=d.batch,g&&g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertAfter(a,d),void 0}else g=d;if(e)if(e instanceof f.Sprite){if(h=e.batch){if(h.texture==a.texture.baseTexture&&h.blendMode==a.blendMode)return h.insertBefore(a,e),void 0;if(h==g){var i=g.split(e),j=f.WebGLRenderer.getBatch(),k=this.batchs.indexOf(g);return j.init(a),this.batchs.splice(k+1,0,j,i),void 0}}}else h=e;var j=f.WebGLRenderer.getBatch();if(j.init(a),g){var k=this.batchs.indexOf(g);this.batchs.splice(k+1,0,j)}else this.batchs.push(j)}else a instanceof f.TilingSprite?this.initTilingSprite(a):a instanceof f.Strip&&this.initStrip(a),this.insertAfter(a,d)},f.WebGLRenderGroup.prototype.insertAfter=function(a,b){if(b instanceof f.Sprite){var c=b.batch;if(c)if(c.tail==b){var d=this.batchs.indexOf(c);this.batchs.splice(d+1,0,a)}else{var e=c.split(b.__next),d=this.batchs.indexOf(c);this.batchs.splice(d+1,0,a,e)}else this.batchs.push(a)}else{var d=this.batchs.indexOf(b);this.batchs.splice(d+1,0,a)}},f.WebGLRenderGroup.prototype.removeObject=function(a){var b;if(a instanceof f.Sprite){var c=a.batch;if(!c)return;c.remove(a),0==c.size&&(b=c)}else b=a;if(b){var d=this.batchs.indexOf(b);if(-1==d)return;if(0==d||d==this.batchs.length-1)return this.batchs.splice(d,1),b instanceof f.WebGLBatch&&f.WebGLRenderer.returnBatch(b),void 0;if(this.batchs[d-1]instanceof f.WebGLBatch&&this.batchs[d+1]instanceof f.WebGLBatch&&this.batchs[d-1].texture==this.batchs[d+1].texture&&this.batchs[d-1].blendMode==this.batchs[d+1].blendMode)return this.batchs[d-1].merge(this.batchs[d+1]),b instanceof f.WebGLBatch&&f.WebGLRenderer.returnBatch(b),f.WebGLRenderer.returnBatch(this.batchs[d+1]),this.batchs.splice(d,2),void 0;this.batchs.splice(d,1),b instanceof f.WebGLBatch&&f.WebGLRenderer.returnBatch(b)}},f.WebGLRenderGroup.prototype.initTilingSprite=function(a){var b=this.gl;a.verticies=new Float32Array([0,0,a.width,0,a.width,a.height,0,a.height]),a.uvs=new Float32Array([0,0,1,0,1,1,0,1]),a.colors=new Float32Array([1,1,1,1]),a.indices=new Uint16Array([0,1,3,2]),a._vertexBuffer=b.createBuffer(),a._indexBuffer=b.createBuffer(),a._uvBuffer=b.createBuffer(),a._colorBuffer=b.createBuffer(),b.bindBuffer(b.ARRAY_BUFFER,a._vertexBuffer),b.bufferData(b.ARRAY_BUFFER,a.verticies,b.STATIC_DRAW),b.bindBuffer(b.ARRAY_BUFFER,a._uvBuffer),b.bufferData(b.ARRAY_BUFFER,a.uvs,b.DYNAMIC_DRAW),b.bindBuffer(b.ARRAY_BUFFER,a._colorBuffer),b.bufferData(b.ARRAY_BUFFER,a.colors,b.STATIC_DRAW),b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,a._indexBuffer),b.bufferData(b.ELEMENT_ARRAY_BUFFER,a.indices,b.STATIC_DRAW),a.texture.baseTexture._glTexture?(b.bindTexture(b.TEXTURE_2D,a.texture.baseTexture._glTexture),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.REPEAT),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.REPEAT),a.texture.baseTexture._powerOf2=!0):a.texture.baseTexture._powerOf2=!0},f.WebGLRenderGroup.prototype.renderStrip=function(a,b){var c=this.gl,d=f.stripShaderProgram;c.useProgram(d);var e=f.mat3.clone(a.worldTransform);f.mat3.transpose(e),c.uniformMatrix3fv(d.translationMatrix,!1,e),c.uniform2f(d.projectionVector,b.x,b.y),c.uniform1f(d.alpha,a.worldAlpha),a.dirty?(a.dirty=!1,c.bindBuffer(c.ARRAY_BUFFER,a._vertexBuffer),c.bufferData(c.ARRAY_BUFFER,a.verticies,c.STATIC_DRAW),c.vertexAttribPointer(d.vertexPositionAttribute,2,c.FLOAT,!1,0,0),c.bindBuffer(c.ARRAY_BUFFER,a._uvBuffer),c.bufferData(c.ARRAY_BUFFER,a.uvs,c.STATIC_DRAW),c.vertexAttribPointer(d.textureCoordAttribute,2,c.FLOAT,!1,0,0),c.activeTexture(c.TEXTURE0),c.bindTexture(c.TEXTURE_2D,a.texture.baseTexture._glTexture),c.bindBuffer(c.ARRAY_BUFFER,a._colorBuffer),c.bufferData(c.ARRAY_BUFFER,a.colors,c.STATIC_DRAW),c.vertexAttribPointer(d.colorAttribute,1,c.FLOAT,!1,0,0),c.bindBuffer(c.ELEMENT_ARRAY_BUFFER,a._indexBuffer),c.bufferData(c.ELEMENT_ARRAY_BUFFER,a.indices,c.STATIC_DRAW)):(c.bindBuffer(c.ARRAY_BUFFER,a._vertexBuffer),c.bufferSubData(c.ARRAY_BUFFER,0,a.verticies),c.vertexAttribPointer(d.vertexPositionAttribute,2,c.FLOAT,!1,0,0),c.bindBuffer(c.ARRAY_BUFFER,a._uvBuffer),c.vertexAttribPointer(d.textureCoordAttribute,2,c.FLOAT,!1,0,0),c.activeTexture(c.TEXTURE0),c.bindTexture(c.TEXTURE_2D,a.texture.baseTexture._glTexture),c.bindBuffer(c.ARRAY_BUFFER,a._colorBuffer),c.vertexAttribPointer(d.colorAttribute,1,c.FLOAT,!1,0,0),c.bindBuffer(c.ELEMENT_ARRAY_BUFFER,a._indexBuffer)),c.drawElements(c.TRIANGLE_STRIP,a.indices.length,c.UNSIGNED_SHORT,0),c.useProgram(f.currentProgram)},f.WebGLRenderGroup.prototype.renderTilingSprite=function(a,b){var c=this.gl;f.shaderProgram;var d=a.tilePosition,e=a.tileScale,g=d.x/a.texture.baseTexture.width,h=d.y/a.texture.baseTexture.height,i=a.width/a.texture.baseTexture.width/e.x,j=a.height/a.texture.baseTexture.height/e.y;a.uvs[0]=0-g,a.uvs[1]=0-h,a.uvs[2]=1*i-g,a.uvs[3]=0-h,a.uvs[4]=1*i-g,a.uvs[5]=1*j-h,a.uvs[6]=0-g,a.uvs[7]=1*j-h,c.bindBuffer(c.ARRAY_BUFFER,a._uvBuffer),c.bufferSubData(c.ARRAY_BUFFER,0,a.uvs),this.renderStrip(a,b)},f.WebGLRenderGroup.prototype.initStrip=function(a){var b=this.gl;this.shaderProgram,a._vertexBuffer=b.createBuffer(),a._indexBuffer=b.createBuffer(),a._uvBuffer=b.createBuffer(),a._colorBuffer=b.createBuffer(),b.bindBuffer(b.ARRAY_BUFFER,a._vertexBuffer),b.bufferData(b.ARRAY_BUFFER,a.verticies,b.DYNAMIC_DRAW),b.bindBuffer(b.ARRAY_BUFFER,a._uvBuffer),b.bufferData(b.ARRAY_BUFFER,a.uvs,b.STATIC_DRAW),b.bindBuffer(b.ARRAY_BUFFER,a._colorBuffer),b.bufferData(b.ARRAY_BUFFER,a.colors,b.STATIC_DRAW),b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,a._indexBuffer),b.bufferData(b.ELEMENT_ARRAY_BUFFER,a.indices,b.STATIC_DRAW)},f.CanvasRenderer=function(a,b,c,d){this.transparent=d,this.width=a||800,this.height=b||600,this.view=c||document.createElement("canvas"),this.context=this.view.getContext("2d"),this.refresh=!0,this.view.width=this.width,this.view.height=this.height,this.count=0},f.CanvasRenderer.prototype.constructor=f.CanvasRenderer,f.CanvasRenderer.prototype.render=function(a){f.texturesToUpdate=[],f.texturesToDestroy=[],f.visibleCount++,a.updateTransform(),this.view.style.backgroundColor==a.backgroundColorString||this.transparent||(this.view.style.backgroundColor=a.backgroundColorString),this.context.setTransform(1,0,0,1,0,0),this.context.clearRect(0,0,this.width,this.height),this.renderDisplayObject(a),a.interactive&&(a._interactiveEventsAdded||(a._interactiveEventsAdded=!0,a.interactionManager.setTarget(this))),f.Texture.frameUpdates.length>0&&(f.Texture.frameUpdates=[])},f.CanvasRenderer.prototype.resize=function(a,b){this.width=a,this.height=b,this.view.width=a,this.view.height=b},f.CanvasRenderer.prototype.renderDisplayObject=function(a){var b,c=this.context;c.globalCompositeOperation="source-over";var d=a.last._iNext;a=a.first;do if(b=a.worldTransform,a.visible)if(a.renderable){if(a instanceof f.Sprite){var e=a.texture.frame;e&&e.width&&e.height&&(c.globalAlpha=a.worldAlpha,c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),c.drawImage(a.texture.baseTexture.source,e.x,e.y,e.width,e.height,a.anchor.x*-e.width,a.anchor.y*-e.height,e.width,e.height))}else if(a instanceof f.Strip)c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderStrip(a);else if(a instanceof f.TilingSprite)c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),this.renderTilingSprite(a);else if(a instanceof f.CustomRenderable)a.renderCanvas(this);else if(a instanceof f.Graphics)c.setTransform(b[0],b[3],b[1],b[4],b[2],b[5]),f.CanvasGraphics.renderGraphics(a,c);else if(a instanceof f.FilterBlock&&f.FilterBlock.data instanceof f.Graphics)if(a.open){c.save();var g=a.mask.alpha,h=a.mask.worldTransform;c.setTransform(h[0],h[3],h[1],h[4],h[2],h[5]),a.mask.worldAlpha=.5,c.worldAlpha=0,f.CanvasGraphics.renderGraphicsMask(a.mask,c),c.clip(),a.mask.worldAlpha=g}else c.restore();a=a._iNext}else a=a._iNext;else a=a.last._iNext;while(a!=d)},f.CanvasRenderer.prototype.renderStripFlat=function(a){var b=this.context,c=a.verticies;a.uvs;var d=c.length/2;this.count++,b.beginPath();for(var e=1;d-2>e;e++){var f=2*e,g=c[f],h=c[f+2],i=c[f+4],j=c[f+1],k=c[f+3],l=c[f+5];b.moveTo(g,j),b.lineTo(h,k),b.lineTo(i,l)}b.fillStyle="#FF0000",b.fill(),b.closePath()},f.CanvasRenderer.prototype.renderTilingSprite=function(a){var b=this.context;b.globalAlpha=a.worldAlpha,a.__tilePattern||(a.__tilePattern=b.createPattern(a.texture.baseTexture.source,"repeat")),b.beginPath();var c=a.tilePosition,d=a.tileScale;b.scale(d.x,d.y),b.translate(c.x,c.y),b.fillStyle=a.__tilePattern,b.fillRect(-c.x,-c.y,a.width/d.x,a.height/d.y),b.scale(1/d.x,1/d.y),b.translate(-c.x,-c.y),b.closePath()},f.CanvasRenderer.prototype.renderStrip=function(a){var b=this.context,c=a.verticies,d=a.uvs,e=c.length/2;this.count++;for(var f=1;e-2>f;f++){var g=2*f,h=c[g],i=c[g+2],j=c[g+4],k=c[g+1],l=c[g+3],m=c[g+5],n=d[g]*a.texture.width,o=d[g+2]*a.texture.width,p=d[g+4]*a.texture.width,q=d[g+1]*a.texture.height,r=d[g+3]*a.texture.height,s=d[g+5]*a.texture.height;b.save(),b.beginPath(),b.moveTo(h,k),b.lineTo(i,l),b.lineTo(j,m),b.closePath(),b.clip();var t=n*r+q*p+o*s-r*p-q*o-n*s,u=h*r+q*j+i*s-r*j-q*i-h*s,v=n*i+h*p+o*j-i*p-h*o-n*j,w=n*r*j+q*i*p+h*o*s-h*r*p-q*o*j-n*i*s,x=k*r+q*m+l*s-r*m-q*l-k*s,y=n*l+k*p+o*m-l*p-k*o-n*m,z=n*r*m+q*l*p+k*o*s-k*r*p-q*o*m-n*l*s;b.transform(u/t,x/t,v/t,y/t,w/t,z/t),b.drawImage(a.texture.baseTexture.source,0,0),b.restore()}},f.CanvasGraphics=function(){},f.CanvasGraphics.renderGraphics=function(a,b){for(var c=a.worldAlpha,d=0;d1&&(c=1,console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object"));for(var d=0;1>d;d++){var e=a.graphicsData[d],g=e.points;if(e.type==f.Graphics.POLY){b.beginPath(),b.moveTo(g[0],g[1]);for(var h=1;hh;h++){var f=a[h],i=4*h,j=h/(g-1);h%2?(b[i]=j,b[i+1]=0,b[i+2]=j,b[i+3]=1):(b[i]=j,b[i+1]=0,b[i+2]=j,b[i+3]=1),i=2*h,d[i]=1,d[i+1]=1,i=2*h,c[i]=i,c[i+1]=i+1,e=f}}},f.Rope.prototype.updateTransform=function(){var a=this.points;if(!(a.length<1)){var b,c=this.verticies,d=a[0],e={x:0,y:0},g=a[0];this.count-=.2,c[0]=g.x+e.x,c[1]=g.y+e.y,c[2]=g.x-e.x,c[3]=g.y-e.y;for(var h=a.length,i=1;h>i;i++){var g=a[i],j=4*i;b=i1&&(k=1);var l=Math.sqrt(e.x*e.x+e.y*e.y),m=this.texture.height/2;e.x/=l,e.y/=l,e.x*=m,e.y*=m,c[j]=g.x+e.x,c[j+1]=g.y+e.y,c[j+2]=g.x-e.x,c[j+3]=g.y-e.y,d=g}f.DisplayObjectContainer.prototype.updateTransform.call(this)}},f.Rope.prototype.setTexture=function(a){this.texture=a,this.updateFrame=!0},f.TilingSprite=function(a,b,c){f.DisplayObjectContainer.call(this),this.texture=a,this.width=b,this.height=c,this.tileScale=new f.Point(1,1),this.tilePosition=new f.Point(0,0),this.renderable=!0,this.blendMode=f.blendModes.NORMAL},f.TilingSprite.prototype=Object.create(f.DisplayObjectContainer.prototype),f.TilingSprite.prototype.constructor=f.TilingSprite,f.TilingSprite.prototype.setTexture=function(a){this.texture=a,this.updateFrame=!0},f.TilingSprite.prototype.onTextureUpdate=function(){this.updateFrame=!0},f.Spine=function(a){if(f.DisplayObjectContainer.call(this),this.spineData=f.AnimCache[a],!this.spineData)throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: "+a);this.skeleton=new l.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new l.AnimationStateData(this.spineData),this.state=new l.AnimationState(this.stateData),this.slotContainers=[];for(var b=0,c=this.skeleton.drawOrder.length;c>b;b++){var d=this.skeleton.drawOrder[b],e=d.attachment,g=new f.DisplayObjectContainer;if(this.slotContainers.push(g),this.addChild(g),e instanceof l.RegionAttachment){var h=e.rendererObject.name,i=this.createSprite(d,e.rendererObject);d.currentSprite=i,d.currentSpriteName=h,g.addChild(i)}}},f.Spine.prototype=Object.create(f.DisplayObjectContainer.prototype),f.Spine.prototype.constructor=f.Spine,f.Spine.prototype.updateTransform=function(){this.lastTime=this.lastTime||Date.now();var a=.001*(Date.now()-this.lastTime);this.lastTime=Date.now(),this.state.update(a),this.state.apply(this.skeleton),this.skeleton.updateWorldTransform();for(var b=this.skeleton.drawOrder,c=0,d=b.length;d>c;c++){var e=b[c],g=e.attachment,h=this.slotContainers[c];if(g instanceof l.RegionAttachment){if(g.rendererObject&&(!e.currentSpriteName||e.currentSpriteName!=g.name)){var i=g.rendererObject.name;if(void 0!==e.currentSprite&&(e.currentSprite.visible=!1),e.sprites=e.sprites||{},void 0!==e.sprites[i])e.sprites[i].visible=!0;else{var j=this.createSprite(e,g.rendererObject);h.addChild(j)}e.currentSprite=e.sprites[i],e.currentSpriteName=i}h.visible=!0;var k=e.bone;h.position.x=k.worldX+g.x*k.m00+g.y*k.m01,h.position.y=k.worldY+g.x*k.m10+g.y*k.m11,h.scale.x=k.worldScaleX,h.scale.y=k.worldScaleY,h.rotation=-(e.bone.worldRotation*Math.PI/180)}else h.visible=!1}f.DisplayObjectContainer.prototype.updateTransform.call(this)},f.Spine.prototype.createSprite=function(a,b){var c=f.TextureCache[b.name]?b.name:b.name+".png",d=new f.Sprite(f.Texture.fromFrame(c));return d.scale=b.scale,d.rotation=b.rotation,d.anchor.x=d.anchor.y=.5,a.sprites=a.sprites||{},a.sprites[b.name]=d,d};var l={};l.BoneData=function(a,b){this.name=a,this.parent=b},l.BoneData.prototype={length:0,x:0,y:0,rotation:0,scaleX:1,scaleY:1},l.SlotData=function(a,b){this.name=a,this.boneData=b},l.SlotData.prototype={r:1,g:1,b:1,a:1,attachmentName:null},l.Bone=function(a,b){this.data=a,this.parent=b,this.setToSetupPose()},l.Bone.yDown=!1,l.Bone.prototype={x:0,y:0,rotation:0,scaleX:1,scaleY:1,m00:0,m01:0,worldX:0,m10:0,m11:0,worldY:0,worldRotation:0,worldScaleX:1,worldScaleY:1,updateWorldTransform:function(a,b){var c=this.parent;null!=c?(this.worldX=this.x*c.m00+this.y*c.m01+c.worldX,this.worldY=this.x*c.m10+this.y*c.m11+c.worldY,this.worldScaleX=c.worldScaleX*this.scaleX,this.worldScaleY=c.worldScaleY*this.scaleY,this.worldRotation=c.worldRotation+this.rotation):(this.worldX=this.x,this.worldY=this.y,this.worldScaleX=this.scaleX,this.worldScaleY=this.scaleY,this.worldRotation=this.rotation);var d=this.worldRotation*Math.PI/180,e=Math.cos(d),f=Math.sin(d);this.m00=e*this.worldScaleX,this.m10=f*this.worldScaleX,this.m01=-f*this.worldScaleY,this.m11=e*this.worldScaleY,a&&(this.m00=-this.m00,this.m01=-this.m01),b&&(this.m10=-this.m10,this.m11=-this.m11),l.Bone.yDown&&(this.m10=-this.m10,this.m11=-this.m11)},setToSetupPose:function(){var a=this.data;this.x=a.x,this.y=a.y,this.rotation=a.rotation,this.scaleX=a.scaleX,this.scaleY=a.scaleY}},l.Slot=function(a,b,c){this.data=a,this.skeleton=b,this.bone=c,this.setToSetupPose()},l.Slot.prototype={r:1,g:1,b:1,a:1,_attachmentTime:0,attachment:null,setAttachment:function(a){this.attachment=a,this._attachmentTime=this.skeleton.time},setAttachmentTime:function(a){this._attachmentTime=this.skeleton.time-a},getAttachmentTime:function(){return this.skeleton.time-this._attachmentTime},setToSetupPose:function(){var a=this.data;this.r=a.r,this.g=a.g,this.b=a.b,this.a=a.a;for(var b=this.skeleton.data.slots,c=0,d=b.length;d>c;c++)if(b[c]==a){this.setAttachment(a.attachmentName?this.skeleton.getAttachmentBySlotIndex(c,a.attachmentName):null);break}}},l.Skin=function(a){this.name=a,this.attachments={}},l.Skin.prototype={addAttachment:function(a,b,c){this.attachments[a+":"+b]=c},getAttachment:function(a,b){return this.attachments[a+":"+b]},_attachAll:function(a,b){for(var c in b.attachments){var d=c.indexOf(":"),e=parseInt(c.substring(0,d)),f=c.substring(d+1),g=a.slots[e];if(g.attachment&&g.attachment.name==f){var h=this.getAttachment(e,f);h&&g.setAttachment(h)}}}},l.Animation=function(a,b,c){this.name=a,this.timelines=b,this.duration=c},l.Animation.prototype={apply:function(a,b,c){c&&0!=this.duration&&(b%=this.duration);for(var d=this.timelines,e=0,f=d.length;f>e;e++)d[e].apply(a,b,1)},mix:function(a,b,c,d){c&&0!=this.duration&&(b%=this.duration);for(var e=this.timelines,f=0,g=e.length;g>f;f++)e[f].apply(a,b,d)}},l.binarySearch=function(a,b,c){var d=0,e=Math.floor(a.length/c)-2;if(0==e)return c;for(var f=e>>>1;;){if(a[(f+1)*c]<=b?d=f+1:e=f,d==e)return(d+1)*c;f=d+e>>>1}},l.linearSearch=function(a,b,c){for(var d=0,e=a.length-c;e>=d;d+=c)if(a[d]>b)return d;return-1},l.Curves=function(a){this.curves=[],this.curves.length=6*(a-1)},l.Curves.prototype={setLinear:function(a){this.curves[6*a]=0},setStepped:function(a){this.curves[6*a]=-1},setCurve:function(a,b,c,d,e){var f=.1,g=f*f,h=g*f,i=3*f,j=3*g,k=6*g,l=6*h,m=2*-b+d,n=2*-c+e,o=3*(b-d)+1,p=3*(c-e)+1,q=6*a,r=this.curves;r[q]=b*i+m*j+o*h,r[q+1]=c*i+n*j+p*h,r[q+2]=m*k+o*l,r[q+3]=n*k+p*l,r[q+4]=o*l,r[q+5]=p*l},getCurvePercent:function(a,b){b=0>b?0:b>1?1:b;var c=6*a,d=this.curves,e=d[c];if(!e)return b;if(-1==e)return 0;for(var f=d[c+1],g=d[c+2],h=d[c+3],i=d[c+4],j=d[c+5],k=e,l=f,m=8;;){if(k>=b){var n=k-e,o=l-f;return o+(l-o)*(b-n)/(k-n)}if(0==m)break;m--,e+=g,f+=h,g+=i,h+=j,k+=e,l+=f}return l+(1-l)*(b-k)/(1-k)}},l.RotateTimeline=function(a){this.curves=new l.Curves(a),this.frames=[],this.frames.length=2*a},l.RotateTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/2},setFrame:function(a,b,c){a*=2,this.frames[a]=b,this.frames[a+1]=c},apply:function(a,b,c){var d=this.frames;if(!(b=d[d.length-2]){for(var f=e.data.rotation+d[d.length-1]-e.rotation;f>180;)f-=360;for(;-180>f;)f+=360;return e.rotation+=f*c,void 0}var g=l.binarySearch(d,b,2),h=d[g-1],i=d[g],j=1-(b-i)/(d[g-2]-i);j=this.curves.getCurvePercent(g/2-1,j);for(var f=d[g+1]-h;f>180;)f-=360;for(;-180>f;)f+=360;for(f=e.data.rotation+(h+f*j)-e.rotation;f>180;)f-=360;for(;-180>f;)f+=360;e.rotation+=f*c}}},l.TranslateTimeline=function(a){this.curves=new l.Curves(a),this.frames=[],this.frames.length=3*a},l.TranslateTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/3},setFrame:function(a,b,c,d){a*=3,this.frames[a]=b,this.frames[a+1]=c,this.frames[a+2]=d},apply:function(a,b,c){var d=this.frames;if(!(b=d[d.length-3])return e.x+=(e.data.x+d[d.length-2]-e.x)*c,e.y+=(e.data.y+d[d.length-1]-e.y)*c,void 0;var f=l.binarySearch(d,b,3),g=d[f-2],h=d[f-1],i=d[f],j=1-(b-i)/(d[f+-3]-i);j=this.curves.getCurvePercent(f/3-1,j),e.x+=(e.data.x+g+(d[f+1]-g)*j-e.x)*c,e.y+=(e.data.y+h+(d[f+2]-h)*j-e.y)*c}}},l.ScaleTimeline=function(a){this.curves=new l.Curves(a),this.frames=[],this.frames.length=3*a},l.ScaleTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/3},setFrame:function(a,b,c,d){a*=3,this.frames[a]=b,this.frames[a+1]=c,this.frames[a+2]=d},apply:function(a,b,c){var d=this.frames;if(!(b=d[d.length-3])return e.scaleX+=(e.data.scaleX-1+d[d.length-2]-e.scaleX)*c,e.scaleY+=(e.data.scaleY-1+d[d.length-1]-e.scaleY)*c,void 0;var f=l.binarySearch(d,b,3),g=d[f-2],h=d[f-1],i=d[f],j=1-(b-i)/(d[f+-3]-i);j=this.curves.getCurvePercent(f/3-1,j),e.scaleX+=(e.data.scaleX-1+g+(d[f+1]-g)*j-e.scaleX)*c,e.scaleY+=(e.data.scaleY-1+h+(d[f+2]-h)*j-e.scaleY)*c}}},l.ColorTimeline=function(a){this.curves=new l.Curves(a),this.frames=[],this.frames.length=5*a},l.ColorTimeline.prototype={slotIndex:0,getFrameCount:function(){return this.frames.length/2},setFrame:function(c,d){c*=5,this.frames[c]=d,this.frames[c+1]=r,this.frames[c+2]=g,this.frames[c+3]=b,this.frames[c+4]=a},apply:function(a,b,c){var d=this.frames;if(!(b=d[d.length-5]){var f=d.length-1;return e.r=d[f-3],e.g=d[f-2],e.b=d[f-1],e.a=d[f],void 0}var g=l.binarySearch(d,b,5),h=d[g-4],i=d[g-3],j=d[g-2],k=d[g-1],m=d[g],n=1-(b-m)/(d[g-5]-m);n=this.curves.getCurvePercent(g/5-1,n);var o=h+(d[g+1]-h)*n,p=i+(d[g+2]-i)*n,q=j+(d[g+3]-j)*n,r=k+(d[g+4]-k)*n;1>c?(e.r+=(o-e.r)*c,e.g+=(p-e.g)*c,e.b+=(q-e.b)*c,e.a+=(r-e.a)*c):(e.r=o,e.g=p,e.b=q,e.a=r)}}},l.AttachmentTimeline=function(a){this.curves=new l.Curves(a),this.frames=[],this.frames.length=a,this.attachmentNames=[],this.attachmentNames.length=a},l.AttachmentTimeline.prototype={slotIndex:0,getFrameCount:function(){return this.frames.length},setFrame:function(a,b,c){this.frames[a]=b,this.attachmentNames[a]=c},apply:function(a,b){var c=this.frames;if(!(b=c[c.length-1]?c.length-1:l.binarySearch(c,b,1)-1;var e=this.attachmentNames[d];a.slots[this.slotIndex].setAttachment(e?a.getAttachmentBySlotIndex(this.slotIndex,e):null)}}},l.SkeletonData=function(){this.bones=[],this.slots=[],this.skins=[],this.animations=[]},l.SkeletonData.prototype={defaultSkin:null,findBone:function(a){for(var b=this.bones,c=0,d=b.length;d>c;c++)if(b[c].name==a)return b[c];return null},findBoneIndex:function(a){for(var b=this.bones,c=0,d=b.length;d>c;c++)if(b[c].name==a)return c;return-1},findSlot:function(a){for(var b=this.slots,c=0,d=b.length;d>c;c++)if(b[c].name==a)return slot[c];return null},findSlotIndex:function(a){for(var b=this.slots,c=0,d=b.length;d>c;c++)if(b[c].name==a)return c;return-1},findSkin:function(a){for(var b=this.skins,c=0,d=b.length;d>c;c++)if(b[c].name==a)return b[c];return null},findAnimation:function(a){for(var b=this.animations,c=0,d=b.length;d>c;c++)if(b[c].name==a)return b[c];return null}},l.Skeleton=function(a){this.data=a,this.bones=[];for(var b=0,c=a.bones.length;c>b;b++){var d=a.bones[b],e=d.parent?this.bones[a.bones.indexOf(d.parent)]:null;this.bones.push(new l.Bone(d,e))}this.slots=[],this.drawOrder=[];for(var b=0,c=a.slots.length;c>b;b++){var f=a.slots[b],g=this.bones[a.bones.indexOf(f.boneData)],h=new l.Slot(f,this,g);this.slots.push(h),this.drawOrder.push(h)}},l.Skeleton.prototype={x:0,y:0,skin:null,r:1,g:1,b:1,a:1,time:0,flipX:!1,flipY:!1,updateWorldTransform:function(){for(var a=this.flipX,b=this.flipY,c=this.bones,d=0,e=c.length;e>d;d++)c[d].updateWorldTransform(a,b)},setToSetupPose:function(){this.setBonesToSetupPose(),this.setSlotsToSetupPose()},setBonesToSetupPose:function(){for(var a=this.bones,b=0,c=a.length;c>b;b++)a[b].setToSetupPose()},setSlotsToSetupPose:function(){for(var a=this.slots,b=0,c=a.length;c>b;b++)a[b].setToSetupPose(b)},getRootBone:function(){return 0==this.bones.length?null:this.bones[0]},findBone:function(a){for(var b=this.bones,c=0,d=b.length;d>c;c++)if(b[c].data.name==a)return b[c];return null},findBoneIndex:function(a){for(var b=this.bones,c=0,d=b.length;d>c;c++)if(b[c].data.name==a)return c;return-1},findSlot:function(a){for(var b=this.slots,c=0,d=b.length;d>c;c++)if(b[c].data.name==a)return b[c];return null},findSlotIndex:function(a){for(var b=this.slots,c=0,d=b.length;d>c;c++)if(b[c].data.name==a)return c;return-1},setSkinByName:function(a){var b=this.data.findSkin(a);if(!b)throw"Skin not found: "+a;this.setSkin(b)},setSkin:function(a){this.skin&&a&&a._attachAll(this,this.skin),this.skin=a},getAttachmentBySlotName:function(a,b){return this.getAttachmentBySlotIndex(this.data.findSlotIndex(a),b)},getAttachmentBySlotIndex:function(a,b){if(this.skin){var c=this.skin.getAttachment(a,b);if(c)return c}return this.data.defaultSkin?this.data.defaultSkin.getAttachment(a,b):null},setAttachment:function(a,b){for(var c=this.slots,d=0,e=c.size;e>d;d++){var f=c[d];if(f.data.name==a){var g=null;if(b&&(g=this.getAttachment(d,b),null==g))throw"Attachment not found: "+b+", for slot: "+a;return f.setAttachment(g),void 0}}throw"Slot not found: "+a},update:function(a){time+=a}},l.AttachmentType={region:0},l.RegionAttachment=function(){this.offset=[],this.offset.length=8,this.uvs=[],this.uvs.length=8},l.RegionAttachment.prototype={x:0,y:0,rotation:0,scaleX:1,scaleY:1,width:0,height:0,rendererObject:null,regionOffsetX:0,regionOffsetY:0,regionWidth:0,regionHeight:0,regionOriginalWidth:0,regionOriginalHeight:0,setUVs:function(a,b,c,d,e){var f=this.uvs;e?(f[2]=a,f[3]=d,f[4]=a,f[5]=b,f[6]=c,f[7]=b,f[0]=c,f[1]=d):(f[0]=a,f[1]=d,f[2]=a,f[3]=b,f[4]=c,f[5]=b,f[6]=c,f[7]=d)},updateOffset:function(){var a=this.width/this.regionOriginalWidth*this.scaleX,b=this.height/this.regionOriginalHeight*this.scaleY,c=-this.width/2*this.scaleX+this.regionOffsetX*a,d=-this.height/2*this.scaleY+this.regionOffsetY*b,e=c+this.regionWidth*a,f=d+this.regionHeight*b,g=this.rotation*Math.PI/180,h=Math.cos(g),i=Math.sin(g),j=c*h+this.x,k=c*i,l=d*h+this.y,m=d*i,n=e*h+this.x,o=e*i,p=f*h+this.y,q=f*i,r=this.offset;r[0]=j-m,r[1]=l+k,r[2]=j-q,r[3]=p+k,r[4]=n-q,r[5]=p+o,r[6]=n-m,r[7]=l+o},computeVertices:function(a,b,c,d){a+=c.worldX,b+=c.worldY;var e=c.m00,f=c.m01,g=c.m10,h=c.m11,i=this.offset;d[0]=i[0]*e+i[1]*f+a,d[1]=i[0]*g+i[1]*h+b,d[2]=i[2]*e+i[3]*f+a,d[3]=i[2]*g+i[3]*h+b,d[4]=i[4]*e+i[5]*f+a,d[5]=i[4]*g+i[5]*h+b,d[6]=i[6]*e+i[7]*f+a,d[7]=i[6]*g+i[7]*h+b}},l.AnimationStateData=function(a){this.skeletonData=a,this.animationToMixTime={}},l.AnimationStateData.prototype={defaultMix:0,setMixByName:function(a,b,c){var d=this.skeletonData.findAnimation(a);if(!d)throw"Animation not found: "+a;var e=this.skeletonData.findAnimation(b);if(!e)throw"Animation not found: "+b;this.setMix(d,e,c)},setMix:function(a,b,c){this.animationToMixTime[a.name+":"+b.name]=c},getMix:function(a,b){var c=this.animationToMixTime[a.name+":"+b.name];return c?c:this.defaultMix}},l.AnimationState=function(a){this.data=a,this.queue=[]},l.AnimationState.prototype={current:null,previous:null,currentTime:0,previousTime:0,currentLoop:!1,previousLoop:!1,mixTime:0,mixDuration:0,update:function(a){if(this.currentTime+=a,this.previousTime+=a,this.mixTime+=a,this.queue.length>0){var b=this.queue[0];this.currentTime>=b.delay&&(this._setAnimation(b.animation,b.loop),this.queue.shift())}},apply:function(a){if(this.current)if(this.previous){this.previous.apply(a,this.previousTime,this.previousLoop);var b=this.mixTime/this.mixDuration;b>=1&&(b=1,this.previous=null),this.current.mix(a,this.currentTime,this.currentLoop,b)}else this.current.apply(a,this.currentTime,this.currentLoop)},clearAnimation:function(){this.previous=null,this.current=null,this.queue.length=0},_setAnimation:function(a,b){this.previous=null,a&&this.current&&(this.mixDuration=this.data.getMix(this.current,a),this.mixDuration>0&&(this.mixTime=0,this.previous=this.current,this.previousTime=this.currentTime,this.previousLoop=this.currentLoop)),this.current=a,this.currentLoop=b,this.currentTime=0},setAnimationByName:function(a,b){var c=this.data.skeletonData.findAnimation(a);if(!c)throw"Animation not found: "+a;this.setAnimation(c,b)},setAnimation:function(a,b){this.queue.length=0,this._setAnimation(a,b)},addAnimationByName:function(a,b,c){var d=this.data.skeletonData.findAnimation(a);if(!d)throw"Animation not found: "+a;this.addAnimation(d,b,c)},addAnimation:function(a,b,c){var d={};if(d.animation=a,d.loop=b,!c||0>=c){var e=0==this.queue.length?this.current:this.queue[this.queue.length-1].animation;c=null!=e?e.duration-this.data.getMix(e,a)+(c||0):0}d.delay=c,this.queue.push(d)},isComplete:function(){return!this.current||this.currentTime>=this.current.duration}},l.SkeletonJson=function(a){this.attachmentLoader=a},l.SkeletonJson.prototype={scale:1,readSkeletonData:function(a){for(var b=new l.SkeletonData,c=a.bones,d=0,e=c.length;e>d;d++){var f=c[d],g=null;if(f.parent&&(g=b.findBone(f.parent),!g))throw"Parent bone not found: "+f.parent;var h=new l.BoneData(f.name,g);h.length=(f.length||0)*this.scale,h.x=(f.x||0)*this.scale,h.y=(f.y||0)*this.scale,h.rotation=f.rotation||0,h.scaleX=f.scaleX||1,h.scaleY=f.scaleY||1,b.bones.push(h)}for(var i=a.slots,d=0,e=i.length;e>d;d++){var j=i[d],h=b.findBone(j.bone);if(!h)throw"Slot bone not found: "+j.bone;var k=new l.SlotData(j.name,h),m=j.color;m&&(k.r=l.SkeletonJson.toColor(m,0),k.g=l.SkeletonJson.toColor(m,1),k.b=l.SkeletonJson.toColor(m,2),k.a=l.SkeletonJson.toColor(m,3)),k.attachmentName=j.attachment,b.slots.push(k)}var n=a.skins;for(var o in n)if(n.hasOwnProperty(o)){var p=n[o],q=new l.Skin(o);for(var r in p)if(p.hasOwnProperty(r)){var s=b.findSlotIndex(r),t=p[r];for(var u in t)if(t.hasOwnProperty(u)){var v=this.readAttachment(q,u,t[u]);null!=v&&q.addAttachment(s,u,v)}}b.skins.push(q),"default"==q.name&&(b.defaultSkin=q)}var w=a.animations;for(var x in w)w.hasOwnProperty(x)&&this.readAnimation(x,w[x],b);return b},readAttachment:function(a,b,c){b=c.name||b;var d=l.AttachmentType[c.type||"region"];if(d==l.AttachmentType.region){var e=new l.RegionAttachment;return e.x=(c.x||0)*this.scale,e.y=(c.y||0)*this.scale,e.scaleX=c.scaleX||1,e.scaleY=c.scaleY||1,e.rotation=c.rotation||0,e.width=(c.width||32)*this.scale,e.height=(c.height||32)*this.scale,e.updateOffset(),e.rendererObject={},e.rendererObject.name=b,e.rendererObject.scale={},e.rendererObject.scale.x=e.scaleX,e.rendererObject.scale.y=e.scaleY,e.rendererObject.rotation=-e.rotation*Math.PI/180,e}throw"Unknown attachment type: "+d},readAnimation:function(a,b,c){var d=[],e=0,f=b.bones;for(var g in f)if(f.hasOwnProperty(g)){var h=c.findBoneIndex(g);if(-1==h)throw"Bone not found: "+g;var i=f[g];for(var j in i)if(i.hasOwnProperty(j)){var k=i[j];if("rotate"==j){var m=new l.RotateTimeline(k.length);m.boneIndex=h;for(var n=0,o=0,p=k.length;p>o;o++){var q=k[o];m.setFrame(n,q.time,q.angle),l.SkeletonJson.readCurve(m,n,q),n++}d.push(m),e=Math.max(e,m.frames[2*m.getFrameCount()-2])}else{if("translate"!=j&&"scale"!=j)throw"Invalid timeline type for a bone: "+j+" ("+g+")";var m,r=1;"scale"==j?m=new l.ScaleTimeline(k.length):(m=new l.TranslateTimeline(k.length),r=this.scale),m.boneIndex=h;for(var n=0,o=0,p=k.length;p>o;o++){var q=k[o],s=(q.x||0)*r,t=(q.y||0)*r;m.setFrame(n,q.time,s,t),l.SkeletonJson.readCurve(m,n,q),n++}d.push(m),e=Math.max(e,m.frames[3*m.getFrameCount()-3])}}}var u=b.slots;for(var v in u)if(u.hasOwnProperty(v)){var w=u[v],x=c.findSlotIndex(v);for(var j in w)if(w.hasOwnProperty(j)){var k=w[j];if("color"==j){var m=new l.ColorTimeline(k.length);m.slotIndex=x;for(var n=0,o=0,p=k.length;p>o;o++){var q=k[o],y=q.color,z=l.SkeletonJson.toColor(y,0),A=l.SkeletonJson.toColor(y,1),B=l.SkeletonJson.toColor(y,2),C=l.SkeletonJson.toColor(y,3);m.setFrame(n,q.time,z,A,B,C),l.SkeletonJson.readCurve(m,n,q),n++}d.push(m),e=Math.max(e,m.frames[5*m.getFrameCount()-5])}else{if("attachment"!=j)throw"Invalid timeline type for a slot: "+j+" ("+v+")";var m=new l.AttachmentTimeline(k.length);m.slotIndex=x;for(var n=0,o=0,p=k.length;p>o;o++){var q=k[o];m.setFrame(n++,q.time,q.name)}d.push(m),e=Math.max(e,m.frames[m.getFrameCount()-1])}}}c.animations.push(new l.Animation(a,d,e))}},l.SkeletonJson.readCurve=function(a,b,c){var d=c.curve;d&&("stepped"==d?a.curves.setStepped(b):d instanceof Array&&a.curves.setCurve(b,d[0],d[1],d[2],d[3]))},l.SkeletonJson.toColor=function(a,b){if(8!=a.length)throw"Color hexidecimal length must be 8, recieved: "+a;return parseInt(a.substring(2*b,2),16)/255},l.Atlas=function(a,b){this.textureLoader=b,this.pages=[],this.regions=[];var c=new l.AtlasReader(a),d=[];d.length=4;for(var e=null;;){var f=c.readLine();if(null==f)break;if(f=c.trim(f),0==f.length)e=null;else if(e){var g=new l.AtlasRegion;g.name=f,g.page=e,g.rotate="true"==c.readValue(),c.readTuple(d);var h=parseInt(d[0]),i=parseInt(d[1]);c.readTuple(d);var j=parseInt(d[0]),k=parseInt(d[1]);g.u=h/e.width,g.v=i/e.height,g.rotate?(g.u2=(h+k)/e.width,g.v2=(i+j)/e.height):(g.u2=(h+j)/e.width,g.v2=(i+k)/e.height),g.x=h,g.y=i,g.width=Math.abs(j),g.height=Math.abs(k),4==c.readTuple(d)&&(g.splits=[parseInt(d[0]),parseInt(d[1]),parseInt(d[2]),parseInt(d[3])],4==c.readTuple(d)&&(g.pads=[parseInt(d[0]),parseInt(d[1]),parseInt(d[2]),parseInt(d[3])],c.readTuple(d))),g.originalWidth=parseInt(d[0]),g.originalHeight=parseInt(d[1]),c.readTuple(d),g.offsetX=parseInt(d[0]),g.offsetY=parseInt(d[1]),g.index=parseInt(c.readValue()),this.regions.push(g)}else{e=new l.AtlasPage,e.name=f,e.format=l.Atlas.Format[c.readValue()],c.readTuple(d),e.minFilter=l.Atlas.TextureFilter[d[0]],e.magFilter=l.Atlas.TextureFilter[d[1]];var m=c.readValue();e.uWrap=l.Atlas.TextureWrap.clampToEdge,e.vWrap=l.Atlas.TextureWrap.clampToEdge,"x"==m?e.uWrap=l.Atlas.TextureWrap.repeat:"y"==m?e.vWrap=l.Atlas.TextureWrap.repeat:"xy"==m&&(e.uWrap=e.vWrap=l.Atlas.TextureWrap.repeat),b.load(e,f),this.pages.push(e)}}},l.Atlas.prototype={findRegion:function(a){for(var b=this.regions,c=0,d=b.length;d>c;c++)if(b[c].name==a)return b[c];return null},dispose:function(){for(var a=this.pages,b=0,c=a.length;c>b;b++)this.textureLoader.unload(a[b].rendererObject)},updateUVs:function(a){for(var b=this.regions,c=0,d=b.length;d>c;c++){var e=b[c];e.page==a&&(e.u=e.x/a.width,e.v=e.y/a.height,e.rotate?(e.u2=(e.x+e.height)/a.width,e.v2=(e.y+e.width)/a.height):(e.u2=(e.x+e.width)/a.width,e.v2=(e.y+e.height)/a.height))}}},l.Atlas.Format={alpha:0,intensity:1,luminanceAlpha:2,rgb565:3,rgba4444:4,rgb888:5,rgba8888:6},l.Atlas.TextureFilter={nearest:0,linear:1,mipMap:2,mipMapNearestNearest:3,mipMapLinearNearest:4,mipMapNearestLinear:5,mipMapLinearLinear:6},l.Atlas.TextureWrap={mirroredRepeat:0,clampToEdge:1,repeat:2},l.AtlasPage=function(){},l.AtlasPage.prototype={name:null,format:null,minFilter:null,magFilter:null,uWrap:null,vWrap:null,rendererObject:null,width:0,height:0},l.AtlasRegion=function(){},l.AtlasRegion.prototype={page:null,name:null,x:0,y:0,width:0,height:0,u:0,v:0,u2:0,v2:0,offsetX:0,offsetY:0,originalWidth:0,originalHeight:0,index:0,rotate:!1,splits:null,pads:null},l.AtlasReader=function(a){this.lines=a.split(/\r\n|\r|\n/)},l.AtlasReader.prototype={index:0,trim:function(a){return a.replace(/^\s+|\s+$/g,"")},readLine:function(){return this.index>=this.lines.length?null:this.lines[this.index++]},readValue:function(){var a=this.readLine(),b=a.indexOf(":");if(-1==b)throw"Invalid line: "+a;return this.trim(a.substring(b+1))},readTuple:function(a){var b=this.readLine(),c=b.indexOf(":");if(-1==c)throw"Invalid line: "+b;for(var d=0,e=c+1;3>d;d++){var f=b.indexOf(",",e);if(-1==f){if(0==d)throw"Invalid line: "+b;break}a[d]=this.trim(b.substr(e,f-e)),e=f+1}return a[d]=this.trim(b.substring(e)),d+1}},l.AtlasAttachmentLoader=function(a){this.atlas=a},l.AtlasAttachmentLoader.prototype={newAttachment:function(a,b,c){switch(b){case l.AttachmentType.region:var d=this.atlas.findRegion(c);if(!d)throw"Region not found in atlas: "+c+" ("+b+")";var e=new l.RegionAttachment(c);return e.rendererObject=d,e.setUVs(d.u,d.v,d.u2,d.v2,d.rotate),e.regionOffsetX=d.offsetX,e.regionOffsetY=d.offsetY,e.regionWidth=d.width,e.regionHeight=d.height,e.regionOriginalWidth=d.originalWidth,e.regionOriginalHeight=d.originalHeight,e}throw"Unknown attachment type: "+b}},f.AnimCache={},l.Bone.yDown=!0,f.CustomRenderable=function(){f.DisplayObject.call(this)},f.CustomRenderable.prototype=Object.create(f.DisplayObject.prototype),f.CustomRenderable.prototype.constructor=f.CustomRenderable,f.CustomRenderable.prototype.renderCanvas=function(){},f.CustomRenderable.prototype.initWebGL=function(){},f.CustomRenderable.prototype.renderWebGL=function(){},f.BaseTextureCache={},f.texturesToUpdate=[],f.texturesToDestroy=[],f.BaseTexture=function(a){if(f.EventTarget.call(this),this.width=100,this.height=100,this.hasLoaded=!1,this.source=a,a){if(this.source instanceof Image||this.source instanceof HTMLImageElement)if(this.source.complete)this.hasLoaded=!0,this.width=this.source.width,this.height=this.source.height,f.texturesToUpdate.push(this);else{var b=this;this.source.onload=function(){b.hasLoaded=!0,b.width=b.source.width,b.height=b.source.height,f.texturesToUpdate.push(b),b.dispatchEvent({type:"loaded",content:b}) +}}else this.hasLoaded=!0,this.width=this.source.width,this.height=this.source.height,f.texturesToUpdate.push(this);this._powerOf2=!1}},f.BaseTexture.prototype.constructor=f.BaseTexture,f.BaseTexture.prototype.destroy=function(){this.source instanceof Image&&(this.source.src=null),this.source=null,f.texturesToDestroy.push(this)},f.BaseTexture.fromImage=function(a,b){var c=f.BaseTextureCache[a];if(!c){var d=new Image;b&&(d.crossOrigin=""),d.src=a,c=new f.BaseTexture(d),f.BaseTextureCache[a]=c}return c},f.TextureCache={},f.FrameCache={},f.Texture=function(a,b){if(f.EventTarget.call(this),b||(this.noFrame=!0,b=new f.Rectangle(0,0,1,1)),a instanceof f.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,this.trim=new f.Point,this.scope=this,a.hasLoaded)this.noFrame&&(b=new f.Rectangle(0,0,a.width,a.height)),this.setFrame(b);else{var c=this;a.addEventListener("loaded",function(){c.onBaseTextureLoaded()})}},f.Texture.prototype.constructor=f.Texture,f.Texture.prototype.onBaseTextureLoaded=function(){var a=this.baseTexture;a.removeEventListener("loaded",this.onLoaded),this.noFrame&&(this.frame=new f.Rectangle(0,0,a.width,a.height)),this.noFrame=!1,this.width=this.frame.width,this.height=this.frame.height,this.scope.dispatchEvent({type:"update",content:this})},f.Texture.prototype.destroy=function(a){a&&this.baseTexture.destroy()},f.Texture.prototype.setFrame=function(a){if(this.frame=a,this.width=a.width,this.height=a.height,a.x+a.width>this.baseTexture.width||a.y+a.height>this.baseTexture.height)throw new Error("Texture Error: frame does not fit inside the base Texture dimensions "+this);this.updateFrame=!0,f.Texture.frameUpdates.push(this)},f.Texture.fromImage=function(a,b){var c=f.TextureCache[a];return c||(c=new f.Texture(f.BaseTexture.fromImage(a,b)),f.TextureCache[a]=c),c},f.Texture.fromFrame=function(a){var b=f.TextureCache[a];if(!b)throw new Error("The frameId '"+a+"' does not exist in the texture cache "+this);return b},f.Texture.fromCanvas=function(a){var b=new f.BaseTexture(a);return new f.Texture(b)},f.Texture.addTextureToCache=function(a,b){f.TextureCache[b]=a},f.Texture.removeTextureFromCache=function(a){var b=f.TextureCache[a];return f.TextureCache[a]=null,b},f.Texture.frameUpdates=[],f.RenderTexture=function(a,b){f.EventTarget.call(this),this.width=a||100,this.height=b||100,this.indetityMatrix=f.mat3.create(),this.frame=new f.Rectangle(0,0,this.width,this.height),f.gl?this.initWebGL():this.initCanvas()},f.RenderTexture.prototype=Object.create(f.Texture.prototype),f.RenderTexture.prototype.constructor=f.RenderTexture,f.RenderTexture.prototype.initWebGL=function(){var a=f.gl;this.glFramebuffer=a.createFramebuffer(),a.bindFramebuffer(a.FRAMEBUFFER,this.glFramebuffer),this.glFramebuffer.width=this.width,this.glFramebuffer.height=this.height,this.baseTexture=new f.BaseTexture,this.baseTexture.width=this.width,this.baseTexture.height=this.height,this.baseTexture._glTexture=a.createTexture(),a.bindTexture(a.TEXTURE_2D,this.baseTexture._glTexture),a.texImage2D(a.TEXTURE_2D,0,a.RGBA,this.width,this.height,0,a.RGBA,a.UNSIGNED_BYTE,null),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE),this.baseTexture.isRender=!0,a.bindFramebuffer(a.FRAMEBUFFER,this.glFramebuffer),a.framebufferTexture2D(a.FRAMEBUFFER,a.COLOR_ATTACHMENT0,a.TEXTURE_2D,this.baseTexture._glTexture,0),this.projection=new f.Point(this.width/2,this.height/2),this.render=this.renderWebGL},f.RenderTexture.prototype.resize=function(a,b){if(this.width=a,this.height=b,f.gl){this.projection.x=this.width/2,this.projection.y=this.height/2;var c=f.gl;c.bindTexture(c.TEXTURE_2D,this.baseTexture._glTexture),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,this.width,this.height,0,c.RGBA,c.UNSIGNED_BYTE,null)}else this.frame.width=this.width,this.frame.height=this.height,this.renderer.resize(this.width,this.height)},f.RenderTexture.prototype.initCanvas=function(){this.renderer=new f.CanvasRenderer(this.width,this.height,null,0),this.baseTexture=new f.BaseTexture(this.renderer.view),this.frame=new f.Rectangle(0,0,this.width,this.height),this.render=this.renderCanvas},f.RenderTexture.prototype.renderWebGL=function(a,b,c){var d=f.gl;d.colorMask(!0,!0,!0,!0),d.viewport(0,0,this.width,this.height),d.bindFramebuffer(d.FRAMEBUFFER,this.glFramebuffer),c&&(d.clearColor(0,0,0,0),d.clear(d.COLOR_BUFFER_BIT));var e=a.children,g=a.worldTransform;a.worldTransform=f.mat3.create(),a.worldTransform[4]=-1,a.worldTransform[5]=2*this.projection.y,b&&(a.worldTransform[2]=b.x,a.worldTransform[5]-=b.y),f.visibleCount++,a.vcount=f.visibleCount;for(var h=0,i=e.length;i>h;h++)e[h].updateTransform();var j=a.__renderGroup;j?a==j.root?j.render(this.projection):j.renderSpecific(a,this.projection):(this.renderGroup||(this.renderGroup=new f.WebGLRenderGroup(d)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projection)),a.worldTransform=g},f.RenderTexture.prototype.renderCanvas=function(a,b,c){var d=a.children;a.worldTransform=f.mat3.create(),b&&(a.worldTransform[2]=b.x,a.worldTransform[5]=b.y);for(var e=0,g=d.length;g>e;e++)d[e].updateTransform();c&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),this.renderer.context.setTransform(1,0,0,1,0,0)},f.AssetLoader=function(a,b){f.EventTarget.call(this),this.assetURLs=a,this.crossorigin=b,this.loadersByType={jpg:f.ImageLoader,jpeg:f.ImageLoader,png:f.ImageLoader,gif:f.ImageLoader,json:f.JsonLoader,anim:f.SpineLoader,xml:f.BitmapFontLoader,fnt:f.BitmapFontLoader}},f.AssetLoader.prototype.constructor=f.AssetLoader,f.AssetLoader.prototype.load=function(){var a=this;this.loadCount=this.assetURLs.length;for(var b=0;bh;h++)for(var i=0;d>i;i++,g++){var j=new f.Texture(this.texture,{x:i*a,y:h*b,width:a,height:b});this.frames.push(j),c&&(f.TextureCache[c+"-"+g]=j)}if(this.texture.baseTexture.hasLoaded)this.onLoaded();else{var k=this;this.texture.baseTexture.addEventListener("loaded",function(){k.onLoaded()})}},f.BitmapFontLoader=function(a,b){f.EventTarget.call(this),this.url=a,this.crossorigin=b,this.baseUrl=a.replace(/[^\/]*$/,""),this.texture=null},f.BitmapFontLoader.prototype.constructor=f.BitmapFontLoader,f.BitmapFontLoader.prototype.load=function(){this.ajaxRequest=new XMLHttpRequest;var a=this;this.ajaxRequest.onreadystatechange=function(){a.onXMLLoaded()},this.ajaxRequest.open("GET",this.url,!0),this.ajaxRequest.overrideMimeType&&this.ajaxRequest.overrideMimeType("application/xml"),this.ajaxRequest.send(null)},f.BitmapFontLoader.prototype.onXMLLoaded=function(){if(4==this.ajaxRequest.readyState&&(200==this.ajaxRequest.status||-1==window.location.href.indexOf("http"))){var a=this.baseUrl+this.ajaxRequest.responseXML.getElementsByTagName("page")[0].attributes.getNamedItem("file").nodeValue,b=new f.ImageLoader(a,this.crossorigin);this.texture=b.texture.baseTexture;var c={},d=this.ajaxRequest.responseXML.getElementsByTagName("info")[0],e=this.ajaxRequest.responseXML.getElementsByTagName("common")[0];c.font=d.attributes.getNamedItem("face").nodeValue,c.size=parseInt(d.attributes.getNamedItem("size").nodeValue,10),c.lineHeight=parseInt(e.attributes.getNamedItem("lineHeight").nodeValue,10),c.chars={};for(var g=this.ajaxRequest.responseXML.getElementsByTagName("char"),h=0;h x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y;$oedx?=H+Z=0kU!@H@C8}1B+1p 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 dr^rzERs0CKwGPZu<1Cv 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≪ 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 Z0H#B*9181k+sPNHr;kr+AISa>|JjdZ<#Yf5 literal 0 HcmV?d00001 diff --git a/examples/example 15 - Filters/LightRotate1.png b/examples/example 15 - Filters/LightRotate1.png new file mode 100644 index 0000000000000000000000000000000000000000..44966cfed50a834b276533f8c34adff03fb5e33a GIT binary patch literal 213699 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 ZfLBC}NRr`DM z31kPMUr;q?)TfV$3f6mHDI9oUg)Px9&W_2PMiTHWz7(maKxP-BN@uu_+yVx#>s(Mo ziDD_8m4<{(jHx9U8+r(jN2oP(mMXO|stz=a0vl0n|M|}|$DSP6>1g9?VHT=2YMkhT z1JT|{-dbH`0wSWwQl)~R>Qfq!Oo`v95LHq`t{Nk{Au}pw$gIDX2)(oxTNMlu&QUXA zlRypPMMeXY1Sq*Pk)M!VF+Wh-N}dupaapph8FbcGx_6stP?b3Q*|gPNMnz^HMC=1C zyij8-3JQ!k(C#l8D#}c$Ho|iy&vX_%Hf>c(d50?zq0W;8G!kA@b3!@cM4MfyCxm3O zrN>e!f<)aep)yvnDn-p`wq+8-s~|&n%LV@CVuXz!Ckt4rFV3o>?eWUV^+dOCA$lV| zW5w?1v~d#C$O;yaAa1!Gb{|XN{X28uLvthecl&c3GifvhYmrwfx{TwNi65Qf%rlK{<)F;P=ji`3In?>W{e zbruyl7iroGaUK3vQIv{zTyw8mMqtBJMa8nJR0yu2^9(kZP{d<_3OtE=AW2dT`S?O} zrbIV|saY*eu>oCYR1z&~Hn&Pb7Kv?QrL;_C7*iJMLzLU*8u%(vL=(88kh-N2o%p$W z17RjW47cmG%%Uqo27R}E10OjSLle=U5xm!n-h~hFz5?bKj>DSjNhQjDvQm7dsB9%g zuSmfZR79`7nESA4*JK7ij^bxrm*g;>v>)%U!Zb-`mMT57l=ihtc<-{OrfY8D3;W%Ys)fx6S>2z`{494>F7;`b@MbaTynxR7YJ`deX znM9tIZ&~4$_abY&6!GE}hXv(bK#ur+ow$~0%|p_jqAwvsn8FfyIg{h<4I|a!2xtKp zZtDT^`|zjt?jZjT_Fu=9{LVd=c)y)1nbSFKffH~mGRh_r8t-;AFql#gvVz&^gt`*t z_+dm+KQ{bA64*nl$_ObLLn8FjJk~AbRw7fE6!k@EX;%%ZXc>X@E_3A(3S;9kOHkI2 z3QE`{=FRt|{7+zlNdjqK;C6P3Lr~tIq-auy26jRv{E{OQrqQN|52@IbaxwwEVCAEt zDOxrnc|oT|#^9nxb(pQl3T6gH2esro;h`U+uW(MAv6NVoSu&HliR!debJ`g_LgI@? zxNaV6GAHGPY~o9&q+woAO0yv`iRK42lJj<@k+x?oe0nX5s+p~hpJzlKQ_2!_T!y@~ zd0Q*WGp?kii4?EmiQ%5e5p-M5JO2bSgTM>a6_u(6ijq}8MHL&mxTlHg6L3k+SFQA> zIrYPbzi82P&rKL12mm<@H5azutE05wBxh^&t)I6noaW1){ z@GSoqr;(b1ngojo=~)3S$hS6e>|F(WgF4r$m48}cZb@|nsJh_ny+Nl3bQ*6yGMI`|56j!J2W}y9%o#q0-@6dqF2E7gV63VsD_6l`aEVDGgVYuo zGxntxUFmFrMD>XgujM)$z&9sLN-E$yp&}|T9*xW{VDk$hc`yC;Vz^CE4kjuR90(<@ z>;YI!I}f@5&W2u?QaBCN1bNAm9}G!i&1I9Fq$0A4h{Y2dz%fSBq&WV7W@;BrUZsdI z5v-t6N&+{()ZbM`hql$OJ{*qK+8jUBunWE=q1Zs z>a}axrfDmUB&YLSHmFiWCKt+9N>kzUqY|Yu+>}{O)xp$?!87lv=v%0bO{0i@z~Z7V z`&;q^`UL@#Vf_brpT6k5%AldpFQ70?Sk*wrB1&haA)=C0&Rez_m`9~}k;H;NrIc!t zOz8|k4&zS;@K%S#88`-j*T<=Otck>l2gx!-`#AGeh}6D<&G_8XxiIT$+KqiRpVGo? zO(`N0I05AUK3|8uwL<17T)IN|goFxQ5JZaM=k=QBW!{~)e8CMCoSF|>1C9!Ts3elH zEr{eez^YjMl4ypISgJ00hxZf|hD#cVwyceu0xZ{t^?bIdaZe`Sn>i~-<;E1t4oodH znZNy`q9-I!WW_fNi2vk5n1G0&G5P`Z!a)mGyYm{Q*n9|?~ zT(P&R)-29#=7}TnR*OuW%MfZz6F;v%Gh!)C6qO|WYPlNvsX6UE_2d5&c)xA~wYRm? zX7*@jJ{K(alUERJxK|DTCvPI^MO^iKJg0V$ipk+q zSL!m@@i5Uh@tR}nh~9u>A9%##FlX;MW7J`1ou8gf-Mg?pe{fwwTff!Ht4DAom(Atw zNvXjNX&9&h?hD|PnZ>kSW2GtXcT_~Ab0O6sD~ZAcil!Mx z#p{e+Zc=VRMN7o0(~@SIkq&u)pN3+Ea|KOA&{57K*1;sCeFkw;s#_OCmMp?cjC|4+ zYzRu}U`Ey3l!}nkrA1Vdm?Ka*LmPVY&ct;}B$C2B%P2R%Sc8RRk(5Bwt!0Bu#NrjU z*fy$wnn__+H0n#MDy5xNiavt4PbG0GW+(#9b)XX{jF(FbqPD_7GyOJ~c+$j3*_`$_ zyi#9te<`hc(`{;%3ft+d2=$R;rp;9{(8LH9SxsJ%Jlfj{%5=Vjor_L(d-eV?LXmY{O*K$wp9>it5&`|_qn4LqZ?g)YRE_H6F8H^4Z^`LNa5y&M$HU%oiPP{E)&PRS6jw6 zq+(b)eHlP3@=E z@%x-`#d@LnX$s%2x@7FADqeLILmFPu0g7}EG|BP2VW{{EBedtQ^$G2ivpXN#@4t5}Q zZvSlVvssu24xG>6!pvnfzShwuIEIB|j=H;3sjK!av}m#yk8lLDE+~Vg8s&=aK+Wp2 zRbdcHgeEpuXj+)cHH>oNoe{>Vc#brO6P2dF87XJGMC*~ViwSjb*-WA)KL60T4W*BS$_dp}=NMe2UG@~5 z>K<^k>1_0#1y;lvAWL9nFdxu3iRv#q=13BUU@3DFbP|$c7%cP)9F?CR2C%f8X~sDj zQ3?ST!*K?E;2^b`3HN&mvI~vUDZKY7#m_Tmku;Ngi`jt!#k7dl#7BstzOwOK^bK4G z40JgZQN%F-YeeEUkqm`9@RiDKbXY~6eu zm+&Zz-%V06u7f>$iR^&|M05`!pdT|TJcnI1uC?pvMR z8MQPOLwiOd$U^tVUE%Yn{}h_aA^qjvWZPz)TrTJ%S#h~C1ZPIOFJd{);G#JhKNV-d zPLv*{@gAV6X1JwyZj=*EQZh|NnB_Lsj9u8qXQo~pOtt{y&+2K&1XBEc+^$PkA{2ov zFYGVES@Ng=!iMZdT=h>$ewq8t`1HH*_j!X@AZDm+*)8Kfy0vg3O)Q783yChcT`L?P zMuxBj+u{0?c=2PpHTbAFE8r`#Yqgxaez*Y%(->FxUl?F(sW8JpiT52-C-|n49sk8I<=&Rn2@jCX2Lf4B4DsT;{TFBn1LG zDyF&B7`KfYDYx=Zvq-x&+R`jRX1+zwSK8Uq1eOU`hUdAKJ@?#3c2T0DvbikLZUOCA zQPpC?nUhpu;%vz=OZ~SHWbu(hnA7ZlskOvlt+LWjpGK~n{W%-1JEw^pAfW?M&!GBU zI)b?GFknt#FoKl24oYmJ7?)$JF5=_yK9Bku@$(}xx&T>}*YRsOOFAnv1l%7;cr+AF z!Fyx}bzF~hnkvIBDl{G`Vz1*pbP-(FMJY}1Mt1#JovI^U)HptbqyXpAUI$|}f@i@b zxD`SyM_I29k4adYYMnkRllW3E#>%lu3mr7Gbqd&!HAeLtW>Q3uDhc~V8?a&iK-XC= z>q=y6oa#PHGnk?0na&0XNgZVd)JSB$CO~tgT99WZDwLI%#92mSth!S7_u3TBTw+2r zUgiZVUKy30K|S-_TSHrG^RWh*3o`{tB${JD0$0pTl`{(5$gNhq(OT=?Ok1wt%@)>D zr<4~=jK@x^g2%RSry&Di2f%v;KleEPojHv%nr`S|AD{5bM6PuiUaEm_($8r_kf9J5l&e8Bij?SKPv~3oPyeFYbS3;AG1sbVTXuj2;cBLX2K&`?U<(lN7 zSy~svM%F0JMEsb`N;N?%IcZ#{!bw!r4--``(-M&h+{6)JAjoqjeB(+r#v+>zr4#Q9 z1QR9YG;~_@D1s(JpmtE#lgOZ1TL-yZXbxvuLUL3zQ&6gGbJ^02R^4TVo!Yc=;)@aefP5;ZM_n{M8wS+4mM$!os|+rq zd`n3+H|^kdv}%l?PYR8vmEM|;DJKvQLHXJe!(=`x@zkOc(O?In$ejm46hlg@<_t75 z5bElJFF7{uNkns|pEO#(W~ zN_lz7ixlFF0Ei%igi2||i|H<4nTF6%Ll=0!vQP%fN}eUjEi+kL0*Usae3_|4t*tX= zGId4}S1K9=stW7H1^obT&kY*MfeOa-4xAyF0=Pq<n!uLV{5lFaSnoxa<=|$NNxcRFln)Jnin7d>mfJh@SGan zX$HSzJDkGbs_-y_(lbDGkN04`!_7D%=8S#F;0X`lOdc+nqPRDpAx&&^BN#=+HEjuF zY0YDa%&sQ7=(Ebj_`b`CuEVmw=zzUZm#VOxx>Q&5w)=?QyPaq)o_os~yv<=+nl>J{ z_r8rpkKp~^%0+=)i}0`KLZ7kyj$cExZg17o9Bx2XwFcW^N0Z}fWcR=;4#B(nosK5p z9J)5A)Hkp53eCBMnk%YEo$#1es0*1wCWxfEvU~cPOz1Y9a>XS0odq{I!_c}pH)Dcc zfXy&a#7&Zv(Rwa@fp~z;StHdIuaT@E_cmyhs^^c8Mv3(-g4nw^{C;le5=k!$-Cv`` zFiqq#DmTb&VkEX!ca}&jaZyt@bbC#%Ql^>PB8H+6(;n4Q7CFX>QZy$O$;aLaq*CQ}uyK$S55o9NEkGQfsW7jF~INyY7@Rd`KV7;%wG1R-MIo_vK z+sLSVd%fsP=lNzq2=NdZa|}_Xu9-k8(^q!PfnM4N%Fhz2A{Nn=nu|sqO*T3s20J2X z3y33GZv|>7M@-T-!>f)!A)%@yruxOXqK5*o!q8QjWspU%=L#!rIGetW~jKFLtLii z%f=KWPKQ$$+POHw1-!l+&VvPP`lw~lRsKI`RBf7^*HPRaY=P5F?hkl649D7w_u1mO zlH!GE4j~dD8BD)u;Y7Es@_}fawR(OGT_t!5zShUD$4)~snw}#177YHQa13w5G4$;& zL3v|WB5c8%Za_wGW}4`>TZsNE@016ZiMMRIa=D2$k}gIj!t;@ZhwA*AMdoq2u9;!( zWz=1XWF$C+_DmpG%jv{1NA+1DZ_0Nb%ah&kOjErr>K#mI3~p}Y0B-P^st|Q?i70O| z|GLsVx2iR%lULex$|^dQ>(aXEAlC#9!Uix;QmY9%X|5(=P$=lmhB^ z#x6`u7Fq_T;N`s$zS)8`q*7kMtG#6jn*IwVt;;RqvKzo1(Ou8Q76 zJu>P(YF3AmmFsY5QJuQ;8Whr&na+3k8JVq+b7W_b?!~2L#Lx*!Dq~TDJ?l76BL}Rc zG<0nb>~0sb0yqJ_KKVvmtCjdS;>I?dK&pZ>JeAFvuxJ4b4SN`bGuUeqoK!@sLB6a9 z8a7@SMspwWGEtEYc^4Gaa{)4NkHEXEC;=uS9up?tLpjaIxVdtmGfXA(ddYHQ81NCp zKmefKl>6*3IR%vKySmi_7n^-Vr_3DNWL##^`{Jf+*B z$!N6;b;qrFfavWzi7v-B*m+c%63g}qK9AR&#ddlzwt8*1J2ir z{9IK+H24_2WP4mSUcQ4H@z}jM&Q-4IZ^>#m+=t&UbyQo`rY?N&*mQ*kdlJT8P=6OT zK&z$_n)SMBpTZH;^BOG(2Tn~K1DBH$b`X3PqRE>X+NNT$VPc*>DHd!@nH|tS3NcNn z>@#a=rNEj5{+X0Y8OH*lK?Q+bZZm};Q%Gzv3*EFUXAWG!dt_y(2;S<0gGv125(P(G z!k{>36wsLvl<~KYRaO-{Z%pZ`HkL(0YME&*2LNG0CB&)Jm?-7|1cumU$FR;y@J)*Z zn;kWIgPMxM#z|>+l`@&3p<7>OPM{w34f>-=Cq;I?eeiZM;_o$hG@L`cWqHec<&E6XZdr(@r5(1DxkcG(n%3a$gAZ6%mvL zm?fDgyg_M2j#6ssNlVuxeVLR;IR)!d=O5BXQ)=DzSZk}wJ-Ij z;krle$CaBR|Bufif5|A(jVH}oR*O~c3tH-PDO$ekuS!vtxf%w3m(rbn<|-rsL)s9o z?F(AQ$Wkun`39mL$R5}!J-C_ZH*tLic0~u$krhm1oBkbM$LuKU*s;uZAdTW}IBQ5I z97F$s^Eiz~0KkgY9}b(c;nZ1w-xKEgi=+9 z5;T#SJKcu?G;LBRHY*SeM&Kn1cnylu)c}zUtwqIL`;x-h;H5h4nipCPVMI7Vc15XqwP`_GY>ihb*ta1F0rtm`qb3Wl&)HLno#78oeCHGSi zjV&^|U6Gh{sRLa)0mHZzkCR$1lK#Q!sEkvIa?^M_)a?H~wxYT_I^TYY`hVybbVVql z04+_IpsPqb^5U{2fv%|j_WkCf_mzk z6`$9paSDwzKXN5cdN@h9K8qE86sz!!-y;8+?L?QH4nwo&>3BWKuL>LB@vC))Zv~8c z8&2TwE++bZ{Bz^OWfgZ0%j@h0E26@vzvpJ6XX1diG8$d&pwMR;=Z-% zGJ+^rv2%daUip_)ZGNtu@luH2A8Y^*D)?I0|NhB}fmZP30A&Df#=Oyx9WLIv$L znXtHOENh30BM&k*R@um2g?0iQR?AsoULY@`cNgqi(k}CKYL_gV+)GHR1$mM>1ruqp z=M)so0fHVTbE4*HNhP7W!eu@i`UVSy;*|a)G)OA#h2|RVP&E zfvH;Rr6XWkX~_s?d#TdX1A{h%zpa1?*{FRO+JcWC!O}on$;5^29*dq+!T9?C$j_|2 zkr->JQ>_4>@vJ(9s84?7#<w?iDeKbt6YHE`wMvN2X<`NC6&qFh8V>6>VQ)Wv1u_DJ1~(pD#@7Bjd{vm! z@$@A`o4=vk?yvFlAU^EoF&T8%QDx2wuFze1*#ks(U~7ybN&lsJ?6SSfB0&WDlLuiEEKETS%Dn89y+(^EJ8 zFnTXOYur)$+MG`P3o?W2DZhLuNsiWA)IXol#P9+QXDLm%d8%g&LakDxo+AB&p>J{@ zp^MTQqOrVNj6YL7DSe+6`n(~^_Mtgd@OULA{E`$RYf2sAwLc)qqLp@1#rVsbGaew}1aj??kIRL> zD{L~)4P@a zPx~U=`=5NqR^F#dyx^s88xA9^0q zixNF}iEWlL+<4vJT}QM9&wCcOZS7c85a|5E7H?qrP9gia_%qP}+!Oi2G0)-({v({v zuiw?#s>2UG=CLZ+&x4nFntcejcHYzU9h7{b$J4+I91UNW)8rjKyJ0X*&oJTUNJ>2^ z(_^H_av22#qh6^{E3Z*KuhK$yO={{4apdUM`MP8fBE|yez)m1%bdEG#yKKt1ZqZ1k z9E}$m4wd87vWqLE9LdGewDlws)me~D5U~6~6IFoJc_|YZC^C|~C}$S~Vx|R=*vNE2 zZ4t=vERqu~Sl2dc1eLGUvOvvvj#8T=NJ^@SfWr%)z37g_Wh5KA5)PjjRuJmr4Kuba z>ltMBzfnUMt+57gCv-G6*rCbXYYw6IIQj-LOX;8?iR$Oc4Cp*r0d+LCTS~;|&S*v; zS-_)s;yyc{lh!O>D6%YuiZ+gE=_=29DXlVn1ha{*$I)=9Y`qSS;b)dB$z7k$bl7~X z4lS96G6kAw8-i@x@SqQTQZ{v)1x*EoX(l4VyX(lC|R8$KY+0+u_FSQaFbysWQ0g-=O`&PnLgz%VE%-R(O{A0JbGMeqlXnYg_ zCYI}JJZJE|fhT^d|Nd51yySWpTps>kP>>{UoviC&@H@^jYwv$Y?Hm zY#GPXUQj-Fu9HqZV$9BX@z|WF71;L^4N3>PpCUO-SLrP5;_&R=W<;VuO>C8H@Ozy=_A=O9C9ITm{YMDvr2Wq-NNf}w4 zaVtsWpPHymLWY4hZDwn$OJpBrt(=Q^jHpoDvI&?%V^;_1N;-?gNbYoC#)N`G$*B0B z83i-+!9j?aJ4-nV=V6#!4tz>!fqDuwjn4TcjBjqZ{J;a$AJxb6h~hdll_v`#J2j>w z<_tz`t8_-{No3r6qd~N#&_^zsT<+<}3Tn}@=S!4_sN=Z(x%9_!E;ELTIwsW9L=RiJ z>!8_ynaB|sO08X??#(@vEx^cg{XZQM&EX`0BXC=UP9jNG2}($LrxH;rll(BvO%jp{ z$fzQ6!bGh?ql5U9GqtZEvaXZUnGIZKMHf}1Boo!q5=4+Sn8Rz7A1o_oP71-Ez}QgIT%eHh=*g*`lr zRro0G_?z!MPcZ%OOG`Sk)DfRdAZ?k&0j-6RZ6KEU#P<;W z7{fny*}Yr>yULJThLYZdpRdFcPtTmkqv11VF2GL5Hof4s$H&4h`2{mE26J#b&f$-~ zu#Dr8U>sg%XeX}ly~awx5}EKK!$^JlFY&bRkMQ~U+i|3I+i^>#9?0qNKj7GhJ*|E& z68meBMowVCZN}m_>8R3^!h2y6Rx4r*bAhC$!u`TdfvJvD{#&I-Xap1ALv5y#_Pv%t zAl;`f9ggEQ;JF@OQ~GWx&?Qu`x4Tp!Z6*Sw&9FcP5lS&i%opM9tf_2-b~2o1NDO10 zbOCxASWuIs&{-&ie*(8aVP7-h%TJ8&NU8iVCbY)KYGUtIR&jpW_e~8}5Gl zId%lGp`EdeVM-@-W$qwMCU1PvHN9kV=l)lqP(Y?uCyt?=l`vEax}_VO`zmMf;_y(zmS zF{!dZot4VLkPdnbq_aB(>u}~xx+=_?<)&oDlom|`RWwD)hPPxK=Q zrt1sKl;i(Box$4sG?seLR8Me)-i*&tg_%_kI@$)(y@bx$;cci4d z?kxFy8_RVclJ~b^nRGw$I35k3b0Q#_nolw-z~{XCdZJt4U001qlg*2I7e7Pm&48>XD7jY@uFp{ zO-@(f2l^DQCR&X)Pq;oN3GBkqM*kdaiLZb*-G;8Ie zV`j8RqS}7kOim*tQ7m@TIT@2g^9B7KD5A;|NIch3BI-o^P;}hWIz=s6z<$dL_E~l_ zYUk68;1Fps?B*OCL1iP>4vWpOGeF**;M({VxCI01fPEz*|8Ye&XZ1d#nV^z9FUc1f z>cHhA&RzU)m~PH=Mu3~ZStt>z@z2{n0vS+2#Y~ZGC)tcnQOYa^v0XtFC-lyii88WC zR%5EDpOooh@}Llw+Z*HsYbj-lKFeU-(?pxOQUb`E!p@Vi-Zwjh1Q`MUuZ19{s(emu zWVRU`Ln6>u82{d!rWe|@8kh3OnKn(Hz$XsnilM|Y%dl6_KzMibENA%Bln;(4uAG1ce>|V5I ziEhC%Xo7_Eb=(u!Z6A!wHF_nE8ArGkTkQl6{*DWYewjsZ9$tQ+e-3B! z!ONA(dK0(9#?J#K#e1H;T$kL6eajf}$NR!D?^%ys1yk_rpIWBhz<~zWwt8^_r@v%n zdZ+7X7=$zygV(9UPF(ylWum(G zm*5;W!IZekxxyTHJ z=^_=>+TKNMYk@ViZJbI$9inQ^tWq}V&Y@+X0*AjCKOYUH6?OJJGM3E}b|9sdU}RTQ zsW*^DrJVp_?T<+AsvTTLo%02~=3oY*D4n9&g7cM!9F^LN3s%}1wV;pZ^mkb{5Hf;* zluBr^4#P0k3rvFBfGmuS{}z>?^eZhB=rSqO)VjTX-&D--=ZO$ z+hhypSA`?!!Kc+S+73f<=#ktH!147SC3oszP8DY9fYGn6WU3GAU!;r((jFuqXIEhG z_U8`U;$I)$N%Z5m+g**QI>Iza9B5yr)sU00fvuV@b{?;qfn9t)R>tcQqyF+eOLW1W zDrdk;{zI1%eGh)QaIYw*%2ID4#yY)Kcg!%-l($_$bOjE0CxU9>knCEz8d(TrIGM(kqml={Nf0Ac_i}apr;d${{)%hhd3t3$3!LEaGKaQKq6gOSEPb+xx zniu0BK8s&1=p06xQpE|3-XQ{PqN%+aSEr5zaq?e?rf`=AU?bMN2r=80p1%4VqT^rl zH1MBr{`(y5emkE3yqqq4gF^<=ruvYRl(aUS(%hjrs;=P8JFN{*_MgnU7~+Zx4bXy&Pi zNNQ20M3P1^A^SVEEF>s!Q5aNGA}tpTd|@1oug3>eM^^CmxPaxRPEF)I$`A%R`Ujzb zoS1=Ku*X3|7Y*6ZmB@%!J4!`C6S=ZPzaTh+(2x#r)TEv!dW@f6Cw1I>`Rr$CF;N2|c+{E>BczFx&-=r!YTh#^{8q-Qb zY*{)nXq|yBj_O}77bpikYkvph0zUE#V$1%=Wcq+cHC(dvlQ`kON0#w@ zoUiXvA;l$~h9&+duTiS(-Xl*wE4ck)I@ZGzcLz(8xC zW6}P%Y#adpTl*4h)6e7h=JYuGae-DKduW`*-|x`%zY?$8_A_t-AICPo8^0rS=*NK^ z{0lFjiesPmvWPBv6BZaQ%0s{FX#49jq@Y-Mi5l!mcg3cYt3qSoa~Szb-DV(nK71g018hS%pheOfaDP!q8CNx?0nlg?o(I9Xi!G5eOJ zB^Q88B2-pVCI(3$p^N$ruHbezW>f1Ws8dcC4TZVDb5kn9ckuVb6U0MQN_)3D9K&QZ zdvGjjD2I81B{}kalnkg^A^%=zS`Szk<-~2aYjKm+2ksP zt{c(52-fEfWXCNbQ{?G(CRt|93rgmc$(5gnzoo*?-;fee#{WmupVym0d6Bl7*czUi z(=F;*iE}usLzIn^88se*Yr*er_~yo}7Z!uYv@p3)v#rAm2f9?EJMa-El_zGG+7jB) zKfo5g`7SJ=ELuVMc>(5V9C!F4SpH)-C`BOO{4epsM0jusR`fgIEZ+I0D7Sq&oWaLn z!+tQg>EM=r@azVxV%W}pT>4%(gZJG`^c>vBiv!wcDZ>rh`3cy}A^h)o6DERFU%uhX zY0@|{j|;y<-@OQ7XBviW@ew$!pMF5ubRMeoR;3MWD0czUaMf#4}U~$ zqm+l9qZz{4Z&<$+2esyf8Xd4|)EqMU0T}-EufjFJUj5FS^}V>PH1Qo=xD%>X(+4{; zcqQKdd7kciIZVK(JyoijEu`H!?fo;vd_S1c{82cCK}0`XLh4DC_I98~Ioxn+a}BQ2 zZ8Xw7N(~_rmVKt}+beSp4o-n8T?);Vp|BpAiWZ%Mz?Wlknl2bJQz@fpB3dAX5mZ(4 z)40~31c~Q@#yF^Gxv0ThpqS*BmT~omLA5mE?@5+uyvC&#A322K^Yjnqt@(P~N<}N8 zMsYkC)*V4idpn#(EM_whQBm|p{egJm*mYzCQA{YubJm*0VUTRUm55H+|63gcaU%i| z6>1KCgdY6f%oJ$Xa%RUX$2A`Gm+rik&L=!9t z*Z%WlM(Vh(is+x&a^VPk3%^$y!u6AJc2L9d&$p-#8EJbR;{Pd)`g278)d4Q^c$(au zQxh@5++&{Rkd&o8Udq1`YSgq;T&pjR_v`-OW>v8=-~SAv3t-eQ?L;=>evygkQw>+rLfpfvbp~i9=pD`qU$c7CD2FZetuhXdm3N8WxuOr7*I$jpzZt z7`H6lyG%_U%l{1Q-@k4r`dQwI4=j^DQ5gdszZNT;kxXZwT+^5x%HJci7{b!H`5-M# ze8-AUz1v8l^U3chl$1Zbg#+I5eB1%#U{m|G9K=0??q|C?f!6D7y1S16nA5;&`?^=jWB?-!`5 ztMnUubwFW->=>ks)FX~VB_ewylaQZ{PC_1wVAdE$dHOU=h9;%HaFEmtoI@^5OH-uf z;G@a1q6APz(@uoTLrEj)Fv788$Rh(A_Yk@xV;h`7;4um$Zev!CSz*Iy-L|T0Q4b+F zf@+5|ps1YGXYUbn8TqKD9~?oeqrjP2Q+vX)fzTvAWR76eoK{Yy3fpj*4R97f7nnm^ ztFj|rX(W_#JwS1dT{==2^FAuQP(&P<8!R}?eb|f$&liYq&+_sf=XpUvu7WJK4)Tym zT5~$F=qZ#Dy$v~9e?i$-+Xg*unSHzy$yvq8*mG-(8RPJ%J~Mrs1aKV zxx`K5#2U;odMB>EClQ7}TcRObGsEdSn#zeq;jNAS`ZNz@x< z1cQ+iqA0`g(eXv$i8!QyZaQF&V1EZ0c&x+HM4O}*>pn{#x0`4 zE=y9*N`WrOQ95TVq+azZa?_2dC@~!s17Su*QQQG#;uOciqEPK1La)=l64fW=2t=Nj zRO5*=VH%bzWdkllXme8j@%fw@ef~Q)fw|;^NG(Zc#kf-qe{0WcV^}C2n(!A*N_&wZ zH1U3j=F@IO{?j;@29vva>RRP#;$BbPn{#^P|KQF)jBKEvRo)QltnhT|voJKLm5Q3} zK-J0_q2%NIQ)5waHt%~j(X+4uHlDVX6mFDPX6UsK5>0PVJKJLorMvO{9+;_jJd@~^ zST(iAlPsP8Tb#jlFy7S@CD87~B?{f$iUa=J%?OyW9iE9eCwX*vDCt4izi)0LdJC`M zht5;yvgOBG1)L^TChcN9(X0T=Q_D1sIV%{vLO*b7uSElfOk)E+c^?+g zDOMoD9a*hTTxSV3IOq49ua#)6Ymr^5!U_q?(kF9KVqt5QwT{K6}m>DuXz8WEaVh z3|u(rLv*Rkp=1nJ8_(;KAxMKdDGjksJR2Q1xpO$#T#!k0u0zqwL`uW}wxdRD+JVJ) zOEhOY9YQeHaYMNsm20M>{y}Ia_eWVlJwg-3@X{ojapwoG7?eXq4_W=LVhJfGr|Mg0^X;GelG2KXdu_c<*l6SCP3 z6M!#z1-a2eBnD45NyuJ`(-ihr&jv3V&qjViz3`_iZt^sT%YOE58T~2h{!fQh#To6w;XZ^d|Hq&1 zC|koFPG77?F@!_j{XJz0$^&MvKtO&LznsEresI^5oVWV7Is*}!G3oQOu${A5s_SsQ zCtzs*`4G{`@4&?1vxi~eVY}(fmvo!2e6}9bAlK)x(D%O!j^>A89$t#roqH3% z=;Y0Yu7Y#Gah$!=iH2}}EthQOl_ix|J|mIR37J^+S+~+}S9`J17?FfXlP^lbO-P<| z7_uy3$ehNYlH%G7gm*farHrM%5N%m(@Jq^!z%LX>@+DJeoE>jb7CoJF^G2_}3Ii0Quep-trmeVL}h&hRpB}!RY2QF^PN>m4|B(&R{(J{+b zJe8z(E6wn-XpYIp@m*YpFd#_4=nvk$obQka+)@i+Or zNX(>cB>qRS_loD|=w6lXylbdoXXl57?-0d!)~QYx@UgQJ{R>G=i(#Hx_fL{p8B}wK zcYOozmp7==l}nGHGKgq@9)Ig*t&*IYV_5U+GFovX_S#-A{kztrr<0#kzM>3o-M}}G z^2{C4x<8i&IIHGBjraPg&uh81@-&9lmMgyhYNA&o6j(XgF<^#6IdLT-fO}w45O6no z_1_#jAH-Gu4aBQ&dNI*Y>?)C#PrEZ_U|ST?d=>E+((T*0_m#qQ{o0>*Lmc z1Ft!b>=K!Ux4P8`^06jt!u&BU z4@r-U4fxApOuvHv&1vI!V7o@`d*7kXK}tr?#m&H89mVq|VElXfHR2z>f#~Zn_OrOt z7vRpd@c!fX7yMbY`h`U6Ujb9_J8&-WimRA?>eX1dIMD~+qKkIr3l-gTf?=w#7k#h; z9Q|_uJ3z$0iFMug5#93|Tx-1Ug8xsSH-iB4z+ZaW_yY`aMS8rJ%TkUi16rr8tzeg) zBEo`mSjSWrbJU)yP*R`5UaP_>)411;1S^%EMA5$?Bg%}|6min?8k2Fn61#%VMDyUiIqYR)0(2!@7s$Og8X6Nq`0 zD#h%Bi%M+H`YeG4yI}G3(KQV70L5GYm6YXKVhEw6pnQ;NaMPd`Sh4k9WIlmecZ71J zA&4HiHllzxqvzu$#VO<-(Y=SkN0je#{GLr?9WGFtoq+7_1*$O5Ph$c551%#PRP<1H zj!ZXGdgmHbIOJ66_4L@sH2dHR=Ou4Z5vv{;mm?p+9z0|~Ve^qZ;3a;~YX2PMF+%{) z5pgEZ{XupT?rF59xI$mqfse)^z4(!Hvx2kNVKE_O=z_^OcypP8{xiOR3MS;62yVOa z9?#joOam`{~;? zh2#u-9h}3Nuj2nRYUBDgs&Q_O<9T1vNMraWjqZ6c?GQ476$m(opG9=|gJoH1=t_MK z&oMmwC+a*`B6HaA{ctq@Kb!{a(kiT$A$YR+<9M&H>hZFZVg8OaFM#<#*l_gYM3?=x zz8@d^1HY=srEPD(d#+dKcKCyy*1_pq`dgmv`&kmyL?66&&s@SbpVqvnh2wCvFq%>} z>)_a`)aEJ5w$3FaUEMkq#scVRU7@OM9J%3~uruhD=A~mt!0eaI#Oe&4<9b91PLKue4zZx0v(jgQ9tjjx`f?r~2EA8kb#DicldM8BbeUVW%u(3QZKsS#^4cfYeG_fvn zZ`3rdQ#p0qZw5N%FX}w*1#<)^EhE@(-^Zw694-0}8_e);HN?<*a{?=D$5cBob-rj6 zQ{0y)Dkzz$&`Ag?YEj=sgx&qz#iX=32@?4HKK07bsoM@J&%&o5u6- zJL?j1!?!Yx;DU#~rnJP9!6|gLWTc;)xctAQ)}jr6|FZw(yIsn27LjwAa2Dx>T?HE}tg^RIf?oPi(xYurURg08sX+mTcXbR4&L<#RcM zz@7hs7U!$(>9e9a>(dkc)fF)858$RR&?1!%hVGxi*5C0ZL?_JtT-608<5jp6w<3rA zx$jjN>VJe2D4hY;#{z84nJw5I%-8^@`X^TrU59IV)nPngbm^usFP%?cs+g7+O`BFh z^*p#+9x%i6x7=lw=qGW(`54}dWBXxv-ocq=%26B`@qpRfF+~m})+=B{(drjzSx1hk zt!N>RM7TjL#G4!w-YSAnGLtX^ThhBqBX*uQxb4T8=@hXY4E!8Uc=~`k1CIPx!q|CUQrMLz?$R4VSUSZLmbPx`T=Nz=rnsD~Oy$CoBaTFp7 z-*3GJ)gjiVbR|OWRE_*D?QjyEMm6gg-s;o{a2*bUI!aExR&ohyXnE8>7`NE!OMHxYG7Au$N7x*%_Et=L7ks~;4S;0yBOlOAPP(cMsC}aaeQO}~4fP)=B@!hqbjArph9DATk6`(=)~`{0LkG8#hHy;zTn#@*`IOH{`NzZfW3ed8 zhgnwrEF8mvlzjMFr(N?(Szd+FpKMcYpiQ#}J@xNw3w(3*&YY@<`1^O{3g^zeI3q6i zaPYzb(dd^w&5kqWi>En!?2ez*QV+KlICCEVW=Zd`T$IlhF-G`x!{45PBbbE~sn(xf zD~hZ6NyK=AO&c()M|KkbJBe6w@VNT3(XD#Jc`CF7I%iee{zhXBQJqZFqiJ*5Upt|8F%`kQkv*vqSI7c{# z`36<{9Rk8>IG+?*)wuK<9HWpJC!1(C;H_lhxxXO&hQZ>|=RKBLaK_MM8ABJ9WN@_z zsY6ku?K+%7z67esbz}>j8A2YFcGgGD+}sRw%Z&5!sO(dTW(h*6Xg2y=zZvq{;!&YO zhak{P9J3z9!}jw@Lkk7y>Uwhq+wE~Zm8xmA$k8m7E%`+~lf@d!z7T1l2-C)U_d}jAt5BthFl?*7?A% zx^&<6BD&xCKlRjkX6HDZ!NS=2>;6nZ)VEp7w+l!H&)oj>TToo>v$(7OgwuY@9cosJ zJBQO+Kxi?|)clAokKCjSnHK=pEH>egFU3lFC*r_A`~oeWasQ8S26G#+vJtBcVkz{( zmV5~I<;6Il3-{sJrk1G5TRZQgJ7EL4r!ZQI;4FC}ZM`5umHqjSE${^T1U#kp(aUvI zyQj4TJlNYHG`vN7)(}L@mWRE)0#Wyo59r`z3wLQP40YS4k5fbYF4&|54n1 zb`CGlzQE}(;)38}uew^%51g6wB51tpWr{lDrZgjzI7Q~j=*s`C@68wfJ8x2>&+mQm zQ_QN$Q_eb2xAmtrv$*&zij0a44M$CT-|1=dkL2{o+i*VXb4t1$oJU3*UIlY8*HS_7 z{yA(aE+fHM)R;PX3>&qoP*u@v7!;IC&ID;P&^98>tj?_vXW;O6?*bVdoCBAk!kmK< z?v*qV@tlD_w-r}7gFI>+Cmr8elpzd7&E(KOIBTV&Ggj9RdIr@9*;A)-T6rLvF9dObyO{ zL?PUa_DPKmpn+4l{*BnD8@0@O9NF;HgW4lFUq-P zE+17b_*jIV?-D~vNA}{-hSkp2teM27@STWD|C9+faXCiDmMMEbozCFIWxCt8?!p1$ zV5edCUWgN{?Og`#gC#TyPx!@cL_g2L?Jin8I@Gz$7nG)z;Z*feT3+_@aS%j*i;K1f z$93(ICHfKqgX1V+hw=_h?a~I4G?#EkHX!w6#zC$>a=^=s(p=JEjK9R0*h{PM0RuZV zx*kIYz$nd;oAFxYKKo&i&%nW~ynMg~^7k@VT?p9IxW@Qe0m+h3Z*lTV>dlP+u^NmAiQ`cAg=s!%B&fkI0Qs zC(#u}UhZ_^kn;~?(vZMfU- z_&hD<$;IJwpuK<E-wSlH4jGj67Hriz$9H)BpMT1L-T#4(K%Bu8w(HQeI_tlK z?Ro_p_=h!)ShAeMA~?7QJFtbWfE{utX!((8T&x*fs5xwh4foLUgJ_R)!AFO5!0dOv zV!)kqj>*coyf7Fd%IPC#1#Rr#o^?7eaNv)1u`tio<`>)a@uONYV*ADrP%)gh;YMOn zuVfq;qU|>FxQE{fQ&Cr{>(jUCLgfsj7n%P?M*rY5zWN(#Ge+QKw!V&J=;as=cnBAG z_}QAN@X#4&B}{|C(G0(i(`Js=H~yeHAMV2*z~{{!C%WwSaO@ZA!haC&zxKuY-iQ8D zBO|8D;4FygHkjG!ivPtMwB!2Ct8=^PSBUO>DI+yt{#GjfdiW+?e(Xq3e%izNIC}VP z8TD@`X*h5D&$xk)WJ1){e_3h`(<&uZ*2%32Rd?R*fzznlKwF=#8W)tog5b;wdmOgE z4@PB~su^MzIftbL>wOfV=N*}Xj~v1Xl_YVYF&(sqCnAT`i6{!k8P6sJnaz27{UIua zXil1;?x&JYVw*XDP!ftWk4|IX=>YP`>BSljP16#XwaMNo{aic>H&4PP9vE{EN1bvu zFs5i8CPQRPo6ky?fIJZQE5llvQ(phd0wR4cQ@%V%$k_hgZC+4n9JDN)&ZS&>;4g@- zdk+sudSu0}fVA>D<4pGi4EO3=we)lBZ*hh0z&hQuI4I%r4Ffbd!)C|9@6W-S5cS~U z#^2I_hXWhV3TF3MDc3JwD&T#?Sv0R>-m^?KQ+b*T?zp-if!TN=R>h`HNPVjFRIFe& z!8Wj87aA|1Z{xK8@*$$P-$L{%1l`>;^u*__o(gBcF~&9=@JA4k4!~I7I%>Q^JxfRE zdl2v3k0A3cSgQTIX?bc@+q#?;tj4{qPCd!202lnPU?Z=^*1PC2S~StOC`wpyl~S8< zDjLSkd7rI)g~H9|$2INjVbUdJ00-WsJvVk3YjH<=wwAE8daoMnwTRyD|9SikAHMln z`hFn-;cd!U0UPmye+DOkY-9y4&bsf%dm$wF+RL;|yYdx06{!n$5*wixn`q^&YJfNW zFpNBmbkeKMW}Y>eL$JB~_w{{OK2wX3`;iUIV;NocJ8C)}f@8S&*Hx`;!w=x|-@`IR zc#j`H_a<<|j!R3d*D#Jv_>6xcnPb@Wn(}EA_p5`bk9dLDa-;jock0p{y3tF@UPj<; z)vu{kOc-UFf(VNm*=|7}m63LE68#Z&e<*tFrczciRw7HIlFIoS$gTsCR5Pa&2GX3B za@uCl2kkq|74(_0kBxdyRB8$^)yK>c9JA)Mqf&o)gY^a0Q8`&$kDQz9a0I@?@LQ>; zcr-|-d~2ykCeJyVw?|4Q1h4OsF&O2Rm42G)1Z3QvK?0dfUwRg2cX$qC`x`A;W4&8j z)V;w`Yci+mkf-*T7vKbI7c@@cN!QQ3o7zsT-)cv=e`<)7IyuPX5yFqcQyPH zA)nB)YHAUqpnY=9tl3wsystG z`w%|7u~UT%r!{_w-sAzqbh}?}GjaHSAHH9SZF>l>8^!AT-X}Y6^;9^6l?eJyAzu0v zg4fscaqY5d44cy*hZHis7}Vt(=^7ioG?kZ;YXfiR)BlHANKVgY_VGoolE}yS@8=BG!D34x&Bu7R?s;^LeB3@jnE| zaPudYSg?~!s|_#uYeZkb#<}{BwS>lHCU$5px2o?X@eY*C6jv0`9x9k*Xum3gIz-I))cZg&osQ1hH&_cB3VU6+lgVHtO;PG zEtMWm-RlwQ*Oif$XIc2~1BO?OpiZgXy!%ruI$m>md z|FuM~#_0^qErycvYGu}E<{#n#vt~sj4!)|RxNJX!Q~gE6TR-)X5OF+h6xEOG3^u|p zz5{lY@hdk4YKpEUDw#OwJ76D9V#)s;m-BYhV)EaP4ttL6fAUI%O5es(ZP62#Y_oRV z2XVn)3dgg4Y?;i04J;#onrC$(SL}m0dgo>@!!tSh=dw=!MT$1!K^Arn9L21Cwr&V! zC7nc8(St?1_WM-y=FV5jASG>hfldrhJg5$YB?NdDfoBxh5Rb!7tc8Kz^kdqA8?%P+sP+m-p`YqBniR;5EF|7bDwv-*8aUfRB9W_jRc9vbP&80ADez(a8rD3eEilUTa}yqQR5@uh8BZ zA2#>ACcjBWtaqLOF)Ihm#_q;zc0K*&A>5%mv27bzQLoz7u?lf_PT7DN>#H!mPe@-s zhVLJN1Ng!PN^5=nf&X{10&xcWaqYj32>#c3XFNnp&k7iu!(ZV7yog~Edo(6lmTeSE zfAm_3Q)uwulSdGJ6VB%-w%M=UzD#Dpj(|}N9L1jetd@beJj7YSs%PnQn2v&@hRh5w zmrMBn92GGvjg2}lW@Z>=hcO64xcNtP;U0o9?O&YOHtuerbHUY0Qh9GXReG8IAPldz1!) zvzNPHt;U@3JwHrsTshEm1@!`dJc885O0$uft z{cp~U{!G-%n2G*w+RkD6m%>DEoJll0dMKF@nC4M@N>A9#aQ}%7IImG&dpCSw>F4qM z4mWzYy-t&leOSZe@LQL>T&M5@%$u_U4rppaIs?GLT4GrNufd)F!^%w$AS?K!f+)_1 z2IBW*9rqd)vd&wKX-qGbF+Le$EqCTemKn0sh#?7-fc`o*NyG? z0<6H-;WGWkon~tPA8`cY4E_@PAF(lA%4MIWc!dnP{g0O@_1Ues2kE16u`g#V>n}I{LyY_CuZ_;F8cc{*!7PtiN8x zZrJcY@@{nwJcP!zb2lo7`L?>-;+L8SOjp^fP}Rb1m(AXP80IavDctoT+fL z`y@8jRqs%Q#*#5b{t5H*T(0(6|GKb#i_xSJK9esd!m@I~n z4n#0evgfX{-`CpnX6^TJ`#FzZANN1PG;<)zo3O`*{cR$O{u^fW8}@t4qW@1Ey;G@h4e2e=429 z5!}z8ZJTg?_tK3LCRO^Ui(r`Zjsdf!zHpIF!#4I}%k?~&0W(X(Zo6EsuZTg{mO&8p zZqmie(SK@GsSFcc|8gxGO+2EGffo{|yQe;_eFLsF4qc(szq1FlX~kIu8_i8H@JBz2 zyLvAS{uZ4D*!PDv;EDGhd4|^WC%%EG{O|GqK8@N}!m!`_6L>#-?gl((;0kMQ;eBcq zO^4gKI9q;_=-6LtM1I|S)j@Fm^}%1$rWhlZ7@5Je*pIa&BV7g9cSXvJxJSMh*bRuHaOS*ot3h=e{}OGcjz4Hwfsx8_sf8nVo)8}1p+=s24GiO~SG6ZFeviI3wPrJ; zcfx>~2;f|+2A-SJ2mjuh&CgM1#ZHFj4A$JB&0se2%rC@~#XLj6nE@N$BOg^{#gcUKKN!WPw`9uXE*^(#7=-ApPX4-@|&!Kutel`oAw><|5+T{>l7)(P3#zL#L*bf zFfdG&XE1nJjUlB^dF!n(a4w2=UDR)g=MF-@A)XwL9YfG77%(T%AC;BD<7z4?s{?6V zHOg(<5eg|Fl7b@$rKiM>trQ?^*kvj!Hk%(QD?-*tesIKcmG2cyZ+aZ;$rB zDqwqynB96N7WQQHZAwC8> zx#nSo(oYTRV&(MjPMq=|BJufin2KAG%&ZLrn@@$Y_fZjai6DEnPX%6&a6F$xV7&>!#`ViVTzT;_ zR}f>98Tr72WZu-D*1nvnZE*^@XaYwElTIQ9O=}U<}cJ zSA=}njOc0aA81XMkw_fr@TPhAw-htY{6su7$5A|QpljZ(+RgX>qLSb-B#<))o<3f{ zin{n$&$YF@sd;{3{-9l+L1H~G{ zfm*`y%|gdG>F76vIfdS+gfkG;_Ja)Oa8yEy)zU&l-_udznU13V@ED@{sJ1V^hmz2w zJsQKOJSSuizN6M3mw93aS4Ye76g^d6qj%_JurHg z?)f4JU9et5WEF!0p^isU+KePL7=_CjbYO**tM0TH5o;eeJsn%^=8@BSPuCK+es zE8EcmgZ&eyqfIedfEgi!Op3EJywsz|-{zCc%sF|1$R; z;F4X{mGC+DR@KY>I_IFyt(+w+5d;QI_;DBy_SpXM%;*32j3;^QNyau~j3dTj92sM< zv9U23Y%(GU0TN&kvO=qcTIyEloL&wU?me^C-uu)&uljY`uRMNgRQG{Wx^#uYKNCK*9ySQK{ZEi>_>5p;DvZQ6QFV$A#Xh zBPn?!X&g4bLXk2guA_0_TzRHLxE!$@V_i?#cga86#({lBLj%S;=w@Wmbh z?qm1s44DC!?q9EANP}9Rq)J*nNTn%P!C7PDSi_7$>|dZ!SYyTE zwMi|4iaJtHcar>k%~d3KG1U_d!%XtrPQ3m!&mWTKIE5{x-wx+U?>xH(pzAFjQH4!} zDxuRi{>|bASr%*wK!~U{qS_840=NWnw)4LxwSxOJ3Z=}_={Nh(&Q5k)V6*I z9KswtpL-bZ?ZYPj7{2KPFa$pezjfKv1*Xo8^EOsVdEa> z#glo=Y6VUeJrOps+S@4EOz4EzEI7$i^Sk!t#T^Dr~~yMG@D{-%ywrEAG*=pZ%up zpB@yAmo%8YI$?jz^|~-^MHnj=Khsfuky)@j#;@SKpZBRMU);a;*-9nFztbT$p(pbh zKW~{k6P5X#8H3n4JY!z`j2VW}pPsg1>5@y zqUPn;=JbOKC0+A8jY8?QGc`ms&^52urb=Pd?r3K|)1*wJauOh!PuAR`juKO(XW#(# z^(pF&>+uBnx&D83-tPVPn*QAn2kN%ZDqw;j4l?_0e7PE-q1{?LFF_7r@LDyalU-5a+?M>+V{Fi^Yb~a;*WpdvL-Fh9a-%I!7{12Yz zL~umY8PBn z7j_Va-8$x0JusRL;O=lD&qucMMwl-X&nJe;|g3l>QtOc-dfaxMC z`GfDYq4qP{5Z1CT?{0j*AA8AO>aCiB4RUt6Xg>X|wPOK{|yH@8t zFLuKo{5dVK6Dh@X7O4qVz2G}p-g55jSq5pbIo1>6Aecxi5fMZE$#a}o7EwqeNyHG- zOfknFdln)>sGWQ^lSBs1A?3iEweafdr*;^E9GNLrm zu|~xvNvIiwz6IEiYrBsM%uJ45Q863SjxYan1rH_4GLpz4D z5thb$fEtQ}@6p-HQBgAmB*tK?Mi%Tb7_`qDxKT}*^R>6A(6bj{ zLGc*-d=#G0afDRcUZmqUzlnvp?cbgq_D5=f?(Y{d1gymzz(RS^m-RIlW%LTab4DQb z_EYFU#BDT^LUKeL)qB!I!>$@s6Ke_SFq@b<%1o)x+CDb8YS_%Yy*}!y#`;!q1hphW2#chJ7NhF^=kYH$x04F^eE%q$at3WBn`8R*+CNoiiVhCFXgV9aeooW3 zv53qX*OvcBbN&zgGVah0MZ^(MK{OHjaF}u#+Mo@jj11_YJn~lUDd6NkX)wcn25{?E zz#-{drVXE0yS;6FkO{#3d5ud>A+3|^<^bjQceq6r8Bg*Y2D+BNS6@0*#K&5SfZJgPq z%wn}gJ$UGy8?g+w;Xt;|NwfR6EP8M=OP+A&9&mGxyx99-oem8sKJdY%x*CJU@hz{f zAH&JP);o$SWoGq*UT2`5#rf#ZE`o+WcgA=^Mjm%np(*bvlJqBvf3eT)_54tud)J=e z=CG@Og($LrN1qIGT5=$!#%fg&ibBn%wxL}rY(y?G#~BM)eYOv#p@!D!v?dxeDr;%u zcS%tT64F?rFjf|$SADOhq`Eml=&OByf`x|tU;k#UW3aHDe&^}@d*J8PBV!XDqbNx` z{g-d20Z1t%vP$y0a|@iSI*n5@xjf1HFLf35bZiJ~m{QN(!f}@_DK;-PrM%42N8BGp zaB~v>21`;#rqXe%F-)6*Xu68xBK8i%hO=f;gXl#H`Ex`I|2!jy#r{#2nR+4mj18Lm z{{UBgxgwD+{}n|dO+Kt1Iim>-jq|OgXlbw@Q&+@#b`oZ8I`h@ zMrD8evkKx^`yAZ08@1lY%ex&ix&Mzeav(?8?*FDpCBjeH`A>s%)$gcLkQ6SqGmTri z<;BWS@Xg=RsDMT3=e$SjhU|IZzS+OTbTjKDMDzs%pl}X(>>;94Cp{3Pnz>Fh3T$Fc z`yZapou$NVmGMz7duWmL5I&#W4&8{$x8lw!8A>G6ax#RUS%_4c!zy?y4(o^a$rvJ8 z3HL$D4`K!VPh9pA+dl-uu=Bo!HSq=iwnE&u=2ao%g8nt0-bo)tsPd7mx?;&8hrzP*Wi|dAt>I-V%4qWtmaXp@oi~a&cya(WPZ2H0? zH=W)SIaDY}!vAx-Mhf-X;@*IC78^gB)HC)jL|D)YuD?}_-FWsvJJvOirZIlmQ%vD>=2&BxUDIImH{3EZ?fUCNVM_tux z&~R~XLdAZ%)D(Mv0wZZ9sh!+a{Q^Z2WuzFn*qO{@TW@z~(&`7N%_vN`iUoDj-k(Y8 z3R6ACAvTb;&XGz=BZ+_B|H$FRZec3I+di)(bWBGI!Q&bzjelpHL7!2y9P0s0`!t}S zPrprJspQt;>7M?o4!N1brCR}yZOK&%1Yy+s_+Q|?7itaRaC-9PRKUXKD@F5HYqhzz&m@@@9O>;@iU^~NL;3s9{JxY^HNj5 z4>!HodX^s4^++d=^+iHr2~n@*y4DBQY9X7AqxnUm^6c)HxD5UQlb*qz&$0WX&_U~n z#+_4%o+c=9)POs~3z3;$yizaq%WY8EJQ-7A99|9MFoe@O!=Yn%@67S@7=s1A*>|Eu zSm=P9*<4q2IENUusNJDo!Imw)qV91||qDTEV!lNJ>`6GAA+$23&VyLM{*=rwR2 zCa%WcP!WA67O`Z~;rx6hgo_+1i^1nex4mSSHo(yXA~Gtr*~b&*w)$6TltDq2?&jpy zjr{`_-Fxx*YV87L-jux#`9+@aKmLqj%?G!rVPesG<#V47HY`o~)ZD`E*6EjGKy)RsNB<`H|a#OIa^k7Dq3tpn@?&3bGEJV3YQ_@E0SYaV@f{G zHy*d`p0f8(C7(|vwTAg5trR(lB7`-qH>7E$bj0RAqklo0CE1j@{^c6U?)wdu#bfZi zZ+@RfwFGf6QO%Bg%FnG(kcZx@(F3DZIy6Kv(Q~H}q4!O+5S8@^48`$}YgA1+Zyr&@ z!w8vk$H~CU{gXul66BPJTAi|L9s zAa%GxH|Gy5qOWjCPhY0Vi_u4`>ednVH;|%fI{6L0GX;r$C%*r)Uvu3FALLZ(;8b2k zKMyPE1U}ybBl6ogL3=HzY8 zZTsvqG-_S}KYx!4_AqY1_aAcZ3S8JnAdlNV>CT?=Mo1~CvZ=i3HIe->oa2BAc_XED z90y3wB8I0CB^4X&*1S*!a_Lo7?l%6r|L0Z6uX&wDAq;gl{>zIy*F*2o?&R%1t=l?& zyB7H_%;R5}>Y*2z4>S^`?@j4Or5qc`8cE70aubav&ykioCAzC-Emv1R!ak$N9*sdr z(nGO>c_vZj)2?b_tJ&*5+t;$BC>_c4T84FtoI}kdI*89_Z2M;tG*ik?G@Brk3`)~2 zLSIPt@w5MrN{~g|87RzKKB$xslv@^(DeF|uyyqwH!d2R>^STLdrUS!y;cH%?5N5t6 zf``pxL>aB6d%CSk^FH-HEoc*+bo`_EEpE}WOVlH$lXT=m&OPsQ%8$hLL8Kk)Xq$da zmGkjWsX@8nZJOeox6bNQP~bzjeOJPO9mO%XajQ<@SU3HEf-D~TRUHA%qB#GNLySdb z;Z(A2>SfPC#POF^`j%^*#c(=aG?W(`6?Alklu*{$U5mH@c-R;f7pe;GZe1l)px#@bB+c$vX+#x)dv9KThG^P5%_ezOc`dg6Mg#nV4%y*aZQ zHE{YMkxQ&A&`albWa{Aabz0#5-%WPMk#bY{7916u^bL*H!@N>9DBb&8n>CxX;14RSIo+X*=_Jr=2eN5aZ zkA-A=LYjAxBg>jvH1_1HU5v(uSq|8t0Z7;It#-Ubr<1+j{-v7S+{}jdm*XwQ^Y z?|m8-c`TV(SdbYw=Q}^~4AVoV_TG(f>Pg7dPvW9&#Ep0u$G`I~w=lGkF`GE@k!^~c zphv>|yIX{SOY)5aT2qi&Bm?S&bGGt=Hv77U%iIaMiyA9>3cbIiD&wkOQB()}1%`KB z+_@fpk9uyK|6e`xi3c)77-dg^*Q(?`Dt!+F$Pt&hjKS3J42vPi`$rP5J{EQddvg7K zNfa=c&!bpYkm(H6x{>*t%hM*mGn?=IBY zWz7?g#Y)IPef$|31IX9Djm~|F1XNg}KLVq4=tY{N%V&Fw>m93dzZ4%1DP(Q_^x3q$ z(P%LB)4x%p0z&f`vZe)Qi5#w0T5E=>z0kjde~J@@N3!W=gOdKXqC1{Gr7ve5dn{J( zF8u|DVII+mciaA5hW&bJeJNY`yRQZhNejH5?iNS1!9NM*5*s=_g7^Dys>`rNj>24# zq=5Q*lz*HnANe^J%RM9QlAc73?o~_&1b!m(8F2 zN60dZtkbaFgUi+9rx96xy;8N1CWa3-rLiH7kprQmjE+g>xb3HOAI#a)u#jr|rgx|@ zVY4MW_SxM}z{B1*==(hOIb6D%U#~fRMjJB^EAu23UEsjHd0&d9tc)a!FDv1HVe}_$r+zNZz#O-KeHLS;N_zT$4*Fc4gw4QBafb0D! zL{$3+b(Nk=Qn^ClTRYCzn#MicvDYmeaQH34ttrS&i_zy^cj2@c=ekjxM8Vh0Go$5iwsDSAvzR$^M zEHZC+iPjXlb_YVc_cyiG|LWgWyfzmgp?u#84HA_M5LUKxqW)(gsp6J)c~4wR>5SVfOFe!RI~#L%4jl4 z+oZ>vlE`Il;Vk1aF>3V$!xo9@;%wz~mg`LF&F_1c)UNq%*|6gEn{5}HO$&iRkD z?nDYb@z)jM9;1Sl*K5b}%u^RnrOmsw35Nj*%|Et?spuns_ur$duWR)OwQ0| z^VnW^-J_QpFILcKLTYHZ^rKt!JEV1&^vmz5(w7FaFX6i|4g;e)J7fTg_@Q6a5t$rJ z#r-qC&uE$jYN=J+rCrcWF)e8;kjyz6 zhqcyVma;-Xg1^243j*8tkq1=Ls7Bs=gL5y#N*TuGnz~GT7~0Tfe~Vz}e}1{b&pkWF zz(S$F!$FZw!_F<*C4G*NV*G0DCy?s;GvN$3Ho$l6)|LGl zT-^CxxN+FW(36W8Cyvmhf~VjUqs*BCi$O^l+R}R`DuGb}>ju(L)2zPF!c=2KJtq>$ zZTP{3P*Qq3557t1vafiPqB^8cp%K0ROedjDWQZUsyn%=J6cfSOfd7 zA+&{}?`vQ|4@#cj*G8f6>yr8@nGS+6aDh07WuojoZ(nK18|J^lFuud^2g)CJiqQkz zK1m~WQz|Yg{b!35q8CeT#QbrJ*d-<}NxxM?R`onPXlb`6F$Y|VxB|BGir>)g zXh|C(I+?_Nx7hej!eCjKAS3Xtf2T+xLR)7agETy)g=(%jMnPPs{Kp|yG@yJh z{OgsUi7c%b8x=sZ!#0&p|II~o3bP!rS?vMKazrR8yN_|`jj@H<362{6O9Wv*w%2v} zejk9%J&N!A#%J~S$8bBgf8DvaLznHs@2`GBrEh%j*)j&Wc2B~%e{9S7qk`ycztr5L zHIIsjUl{2fBex}ro85GGvNSI?D!`Kb7%tpB@H20NeBOe4{s{E<<~tOeu<*cf<@=Uu zo|2vpkqe#XxoLS>G$y(z7;c-qNO`@A97-egf?k&JlsVMsCUEhQ`SkMF5(ej`SbXS1 znY7$b+@DDl;TyT@Ou`8C)m?@a=Mi#*qK3uShsXd1pgsVP2L=FMbOx&tI)m6R9>oY0 zJH7l7@$^GBXmtQZ0RfwIT-Hm|?cGAYrNTvO#t@duIeNcRjRF}9z4kcxtclmB z<@iG}hS!J;!WbgdIedQt5gwk)Y24>|+%A09ZbQK}T|R{E$+kO?gA&d{a8nNkxBS-9 zZbxObSQB7Mh~2<1{E|XBDVuE4qE~#$Pg*_UvpS@SMdR)N0TQuF`wLdwq=+1P+Gp<3 zPULx9#^skOLXGmlk(f;PFg3#{n2LY$uudH$YKX|XC*SVejqgy;zH>&`3!Ny|4}?(i zE@fQ3>=46(wT2SfJZdn z-x+~W!rS-DpiKI-4JBh;U`S!1ekg%=s)rp&VS-4&`%JdUsKbR^^$3kZ@Cke>>q2CC z7gNsog||L65Rn8f=JaDq=+VR|M5N82!9`glzSKl#Q<54g6Xjhd5Y(8Hk1)Uq`=Y?OrF4Piv*DrdXPZ91bud0ea)h)LTWGi`(>`%%W#-VrX`W0>JfoaQF)9$|)*Emx-_-NYE|i}6?D{F`sVRZ}u|={NGn(aKsyk*mm@qhs z->i8c>9?{hMhA!S{ymoVF=_tvmAVQ^BtS_%iOaAD!P0MDtwVOUSo-}CF5~T6pABPx zgWikfdFPsQcN*K*#06uV2feX0SSZXaSk1;qaJ3i|!oHg}4+j|e3mxpno zegty78yEK>Zu{el7#Ui+!SxW{*kE>tbtUF!LrF7R2%;40>S>KVrD|k&6=9`VbXNrF zu3U1tce4k)L<6VpfP(0FD0Pp}G;;%M@w#1eGY4z%>}qZZF>#ZmgM$r$dD45&8ZZbg zZI%jiBN~b@uaD1|0?Kkm2BY|XLyNvPMU1aAdj8&0KElU{zp)as#0Ui574OdB;RpFX z6~7t)zRr5;0=;RX*fkvc1)ehR|A4Fd0iuWQ&97K%DWTm~A6RS3yOG=gpCta;2^4kG zRUN@D`E1&hcW!2Xg{6vOhw%wlbu-7b(2}I!9C`}NsZmHFp~WKKKNn+boVeD&D?-+W z^mtjfAy&k%#y!DIoigwv9_%|BMU>HS`rcpJg3em;49f;cqvxH+Bg-v6jbBc?SuJA~;L z(x%W;XWm|Bhv1Oc+@wWjIb(cw=>19qL1li79m1R!zC)wu_DN0uyztAquGj?0qBIM} zOr0{dN+b_wgWvQn-7l3ly?bHxg;RCuuPL1hBX-UhVKXKjt^0pLQ&KcgT(7HNsq+_R zU?6f__WosB(|F>oRnW)%$Dvw`eEda1NlTTO63RRO(jwrPB+Ie+Zj%D+dhAM#5SBlZ z0Ie;H@s~KIt&q~o4yhzknBNWM|Fd6L!*ldT+$LNa`qz6mIQQm{RK@6bKF_)5J@qUY zgL$0DyRKZw(6yLrNGjyK`&AG;4X z+Z--nWC%BSoX??05oZobCn5n*ET`4B!ERNR|m z6fo*a-|w<8qN$w>h9akj-!E>P56S=P9OPUuJcJO|F_( zoc9!?gR8iLCK41;JBbW>5k4_hwA9j2k(Ve!KGXD$b5*CbUj+Z3@-X#|-zv^B4tgy! zyKX+Yewi^L<`z9Lic?Mm@0(gQF14%KL%}mzkYaPtdV;5sDAm}q<`6pLxRs1B_>AAw zrXUh94wG|k=0M>ZE1Rx)qJ;8p!wU{>V#vFd+?in)!K*j%du#^&Lkp3@y!fPj%bRi!JuJZF-V&aFA(8R$=2GFG_XPYV zxD~G?R9O`!>7vK()>P^mXt?@nt&fqnh{fiu|549~3cE=J-PM{hVqJg*;Fmq71!1nI zqaW0{t~ZDBa`p`s-}Hp;MEhjLyuw`+q5J9iFa!KfT*9HKdZx~NS`r(SPc-b z>6%yQTmtq4@oJA;qD;e&{ec=MfsN1W><>mGoEiQMpriqpphWPW$DF-i_<0M`HV);! z^V3x|>(nh!KajlPCwj6IxWs>o!@7mR*ctu)6x^53;1u5UIsKg6MH>!igV{86*bnSg zDP<3TZU$}f}(+zBd;ey=EpAN2drsGB~ zWy4|t1?=6aF9kOa191}W@A8!juwbAv8s{2Zt`We*H>wF{t6rjg3*;_Yw(r-pFNX$(Qw^{BRi%=Sbkt-zz4Y~JSeWadcu40rue?_G zal#rtWxx)N!&j7+Li&CizDR(LM#yWs{p>$3|rbyq69YRMCOF?=P z5GBw!u%{t3<++q1^n@lJ{J7O6rp@h|bDc9yd}m&eO1_3i-f;sPm`W<$%t06+gahlp zH@LnvFcinRZc0H95q&fXCB5_Kw9dvJ1)lMW>y_4m4Q7lS2pxUuy@ohq55d1xsteWy zNR7d%fzSQ8LRNVlsKCdMX@l9uAJuWUoe6c+IE07u6Ugw(X=l^W#`R2lB4qzFdRN%- z&(zi9@6bMj2qj%{oz6&@J8JDT;o=OzMs+Rx-HY@63V(e7h04s?`pI@|Aa?juCAWBa#0s{5soza5|b=z|gyx|7fO zP8owjiIP6BL*1nEJW$e(K}q@d^2c%hr!TN*>|}0@I|}8!?!hEz4;O#TUxM}hB98l7 zi2KVRi;uj*yA5|6?3{T-p(Wc!FU7l6JII&f1-G0Jdzl}*{s%I*WR-VgkL7M;r-#7h z;_+wj*jb1pK)W-?_XecHmuFfQP~6g5T$2=!FPOI}v<9FazIgqmyemd5rYO9jTVmI;&n?a6x|t)Z z5+{vfR8ll9Wi&1^XCJZrr>z!n!V1Jk>~XRO`#+G>2ew#!V2jlUR+=G5?|LmM9H$un zj;kmj#u6NVCsFvZ*I+g=7O{{V2j{F!n$Y4*gQITYJchI>E%oD4;}UwJ1ea{Xo5I)Q z*5@k1@OG{FzD5Gh?xo$+pbx>G=-ss2Vo@Argu}XvLEC=6=5ySz7;5KX9E5Rg8qY0F zBheVpC~z8Yi3p;_)YF6@n#y8I>J>yNmGf=afdMII5n=W>p-f8z9EIEZV}+aCU%L5| znIM(=SCnqW3%sk3mTux+NCCX9rAt*YQ{+i(clmFs(kC$-Q?OL(G!8t}Ghc@>csE>w z|DuC%#{Wt$Es;pXNw6*;^#IN>zE`2yG$i}~Py!%HLA6Ln{X$LYvi3lcr_x{jGUH8} zwy9<^EW`1b3Rg5pK^Z2_HkX4iMZ z7#LB~2X9achZX06k}^(bnTf0=^X%?^=mHhG4VpQH6RI(7zu#RLt@XFKQNMq^cYRnx zKYkw^8H87jlWjMA2to>(c07We|wHcT=?X|u!E3q~IXL9qLMx-exBs}^um$m&6DN*4a`>!5K8w-VdQ`Xyo>knaKweNtu1OA@mlE9J~zGSf*-5&`Err z#AZ3WbwVPAz^-T})IE_)ZGA0;>{%FvHWci7~mAv!B=QWWrC`mp~*FyN{oOQDAEfsjkPd>mA>( za~3#byu(NU5y*2tsc>(O>14M*K^`2R$?wVUOO<^hpm^%1@6kfClxa#WP?IE&Gtnp^H+3j7i2FRzS;q0s*l%Kl$JfKxFz!&l&^9>=Qr{`++G z96=DZ=WEWr`BH?^SY0~~t6MXM#|N;Qe&KH2)-jyk^6yB^0Qcw+!jw;JIzRtVbmXPM zET}cir`$q1=<|5-%yFHBUcTh6ZwLI99lx3Tz6a;sqZ=Fk#zTGK;aiGq`&~PNTeCTH z>u{wNG_^?`_S%T!?+VV3Pq81YYQPJrp?<7qCTqdKv6}y|I(Bh z6pQ4pqn90t;&>k}YdrT28H&4CR1IJ9^n?;YuKkpXK zIcW7XgT>zzi9wil)nu}GTuYJ$w^R1LI1=-aJDYAJBu1;(Af!>kV)ca3-If>S+O|4@ zrQ7T~V!I1{Rfd?g^VNNl;&SLl4=NI6g?96%3FAE^QZ1sHBqeD{a{(scdX!5=`G<0q zri}!tG_MVD^miF4WOImqr!=SPSdebP!$#x)ha|7hyXi+uH+%^qhv6Vy(pUbk;KuGN zjhVL;sOs3)0u+CcZhjgEQb!0~_E(_g-mkRN^x8>-&A^3YD+izfulj9;pK?|KA+2m0 zW5n^$8?0yGAECam{?5eZRDhu^CdG19fgo}Wz`8E{UsuU&jpFrb5LvU?NP zC2756GWaMyU;jWQiBmfiMYJFVZbNWBbQM$*o^OgnZt(rTg7W`y7?J@<#kd!`;*&k8(3-YejXMRF3)B7Z5u0UW&?cNkA1t1fmfvEyKpaOGGiP)x1p4wHJh!` zaoHp8{F83dr~nt?*o`{2O^BKlL?ohu}7hzh;NzX2`Vm8w^ zg+V!~0K7#6;__!*$f9gB=)%{WzG2~Mc(UBQ#;nRsOITc(QmVYwE_O(25#36$rYVSC z5vU^+LO5kyjPN~5xL5uN9P_)BM@r{C#$|v`PWE(*LRK@Lg;~?hMvZ=B#k6mUQ=6g zPi=69+)yCtDn(ZF!BQw*2>;~ROpezitdtx(orb)wGWtN_E*W)c}U?0;n}|qHm}e0;YVOd8u-nv(4(DI+M{sU z9nSso4bHs?t8VGIO5ibk_C?sYKm4>x=3(r29WKk~)_l8-0dDhySi)aerS9ALY6V=? zFJd5p6I}nWh{T2U2SDUnWU$3j-1Vs*->UZ}r0+BQpqiO(6RzpSyRHvreqy-`S1*C; z9cG-E4<3O8tXZ1p#8Rxx>TV96@0nJ^Wu45;m;J8MsJZF+h9Gw6<(FI}^#O#M4PodE zlw=llS-4|vU(!2uTzEAe(St1m{dvRkmvP}&Ir!k!Vrfw~SmveB?0{DSIw>ytSO6x% z$Fu-bzx9~=(LBHErQCA)nequmLr~;`9MWwV3^bvP4iV=-#gk>-CNlmVqVbV$wKN|c zQ$j)O0wEm4@gB$Tp2Bb99D&FZ8Z}SM%dayZA*P15T0{_s;AB0GZoa2&r|c=Xc9Q;r zv2z%Lv@3WvX-JzhLo$${g<>&zlhr;>Cbf`GQYYw3ynADI?QJcJDV(UBV{zW12Cdns zf%c(FA887xV~RbZ+f3<)(vV-SYbew%7(_L$n4;{akL2uwrJ>j}(k`poLv)3`Mn~L! zDB~H_G(r@vs*V+&+pc89Ao%JT?R%hM5Q8x*2BIuyHLAeFONyxpue~Y8uEg(zE`!fG zkv<(s2_5-RAw}PHuPB8Mf|L=nPX^&7I(@(LbY1$(+IdXAAWkyd^5fdQy!Umui;N2P z>6Kmgazz=DMw`Zfy*}(JCjE8IdBclK0?tg^){$LU=- z+%Hyo^z?PsecYP@$EA2Xy#76KrFI|I<bGA1meOz6_-^qYY;3accYUn}cwxUjJd0 zLY6(>itXO^B)lzrK1<#s99w>ubASEvZ?iGr@Ojcb%e6eu_qb|aZk53t_igyV1sVdw zh#Y~jUIwFIn@B8X%Y3oYvY@S?!-^ICqss6Ky#Lg4@5To*0&(1ub&@9WlUr+UcoHH= zuY3>^*si+<6z2>fo$U-a6y6ZR=O9Gpxj;IZ+R6N&E5yrhxH@i0Q4;Z^44qnp#p20o z!VlyOffFcwOv^AYpLxybW6@>s%*26r1`G71wt7i%x2#~BlJ-%r-(?Ydjo3iNw;FRP|vi4jb-W3`4{MNJ?l1?;SPI zA3jMqhMye9Hd4holX%><#1O1EkNZ+rv5u~D)jWh+PtHAC1WH==DBM^)j@(0WOl6uT z@=0x>nM4q2Iw|rE#rX{DEyZ;zsWZ%4A-tCKAk>orai8m@tzep_o6Z+$RJ4~5A(3WZ zH30^T=qL{{z2(*Dhi)w)7)s2%{4yer3}IyVFvNsX&;~+DH3l^VSxhTMLm8scsSJ>p zXd%{(>iUo*?tnTG?0pd!>#U}^*y$y82Qw157ojtcP0vX4TUjU_NLFjQPOBw74$YGt zae`(Z@3_H@rK_zi+|(nGIJl9uCBb#}1_4H0`@2Dm2j`8mhk$JrOeK+VmBxXCpXQ-3 zmhIAEHXNmopR#RX`+t5t77FX@*qnbX7{wb+rQq6XR&fLcaINxx2i99qZ>0fF`v6Z zDE>yL=9;rLQId5QY@zgP8KIx_{51mbdB3K5nA$0IQ5SF-G73L=r@1J&*ool}mV=i( zd}f|lMC>?QXcp23TfoHl+X9t_E*GjsBkKbY55HSc)EuyCe{1^6jIO#=FR(P@6C(gQ z4nj`L%nXZp)uQJt&mT%l0cG6+qK5IVZuo;TWK*exW3TdYpt+}WHz(Ky6G^l^qkGbtX2p6_-N zV>Ow0`{{mTFTo0n4#pCL5Tl+fIlq+2ba_u>T(AbSine26-$ACHeitZ^8B{be3Z;7E zj1;OUz#;`n|G27{EZIf9=UPBEhXF6jB5foz!YwffgMs^b#$ zTdsq5f<_LZGb|}Yp~`S(9DGZ=qM@>KJa`w->T9L?JnI_H>hEF@?D4&js~d}T=B*rg=MK9oYDd^(c5cYskH>M0I=M(WQWdQ ze(Zl+|7E7ipG9M#%Ra}*+6%0o$^t?N`J#?m828Pw889mZdbPEr||hs7=X{iU75$}zUm>}2C1yya*cEU;t_mr z+<1?0JYz6We~(M@D|dXmi~$a!iRJMwgepV+{0(MYW&g_bc;6wjh8H-1hRGLNvsK?P z@uN!ZQ=tx5{T~ZQZ2a zJaNKuq!v(mu`PK}?~gxX?pEj>N;Munw|s6a;u?m(OE76&y;?W0loO@C$dpx-hM=tL zl**7v4p}0BuC^~-1u3$qAXnHBy+v&xi^joMkkOp!qn0w4d91ARC^ip65FS?_s3bn& zG`2C0eaG>SMdD@GOwo_3_a7kAewP`8t)^__JY?Uayz?GI;1ZlgSQtlk3&L?I&U{iV z_C1O|qMc_FqcGotOp1L4!&czl;;OmJ(+PAooru;*j(f=>2Epn%-2(9%3qA8GH!pSWr|zpNW?w?AJqM-x z{r9UYa~zKQ?z^1(eLUZc-|l?k+hGi>to7k5ojcI4?%nweS}Jo6FAjW*Y{ew*;`5+*AHWwzps*$fvh0hvyX*S!ot?ScFqyf|(wdusLR~@+ z1;aYTURgAjzvWj51I+d8Gm8|uj+XP?e7xdE(?J^cU3zzX zGs_Hyn(*z*dE^TePj|6KPt*#)VpuXC(TLh%%!0DLA0vbidHREO=R&ndHU1CLgD-=6xIjA-nqCaAF2IH3s(dYzCDaLwH>ADeUXjiadNm0c# zQmj1``noJtt|FSPyAP~4%DAjI9;2(tTFC;}AdG^?>Mo)5Ink}qCO`raq{bk|VOHGs zb~fY6`HssvbFMwt!KUY3V^!e_L=J<97RszGQNqB6Qj8F#}=iZIgv=UeFh9@j4C^UWl6A$Qdo%k*+K1lXK z?C&f1?jLST!`m!36pwoTk`ajoQ?KwmsSjkD!;er7k^f9Pb|a)uyzNYz zwM1_htPccn_8A4x8_ZcV$U`@#mpXwnC2aMAOcos8W0!HE1p+w+qdX3rN6g41Mk|6& za#E1f#TdaP!MvTIkxld#r9fPD7E@segjK#-818&da=i5XwHSO@;gy0CVkB@9HWnUU zHj|`SB3FK9B0gYBbb}cHNRQubOG%+Dn#qLy1oC+*xq z4xz}GG?zpaokjeLGESaV(CXNTQgu4?$B+A=G;c-|@a!3dYiQE_RPd_?MZp z5+jA0${HM&lJ}fQ9YLuz%?q!ZNFw>2cmpx2G!&6UsaC-DU56#N6qdctG%5@hHb|YJ zUG+}Le;R~NVe7WE&6ncP38JYC)tn2jv?jX7asys;bZ&<9HVonSIG@s@2dOvUyoh4T zNXZZ@GN7?dD-f>Z2V=Db+?x*N5w{^1I;1%oWn~w&KRp%{=Mds+R;e zxfgq6G*Ax`6>zlY;07Jfnb$8~FA^JgjWw9{vM-x5JdDp*6Ff7c-)9PZcDXT?rW`&v zv(N3)w9iW)PHF`(4nK(PuYlxDU|an}b7D1q^p!UF-sS>)5Vma@r12Kk&*8p|!R^|G z$m17bJiZrtcokM}YxgrX2Daqhdl`&teQ{&pll0I{Mm<6Pw&mY+72#4#hCFy#dU!6k zKn2}}UoC^MOkf?ZY}DLwD9!m=UhnUR5W&MA!6U#J5WQa#$_b&W*BT@611_jAGJGxwh* z#a1?dv1lBXuadZhgzCX~d_v@gEA@SDM?C$g8+`ZJv}vrS1|X95^q?2e*&Mg4)CPFM12Mq3Wgz+WW7wi z@1pX4nTngo9T^(2-1%mMj9zNd!DhE`M5T9&^ZG0{4ZG|~<-`u;nDU7YV)H%CX1zmp z;`^!bm`MysqbG$^w_{#s<-u4ph=uCOB$d_Y7S2!ha;|z0tED0+veKPp1htd6w8)^) zzLBzK1a=m61S>QLOJ|Z!qRY%`2GibgW&;J*VehT9>&25L8x*Zf1=}pe)Md z^7(G>LVQ!=`4AzirPVVG!DMm~V#l=C6q>h`RnEv=Qdtd6BS~~IAjN3b4>BAJi_J6? zOfA(A3ACDXGSzJ!+9hL=umi);W@mKA4Ggs0+~Gp%6}A4-jb7bx%Wq_cvyh2`Lzlu> z9r12vBDgiz1wkFnk89c}D4yeK=|qBSFSC~qFH$td(T(lWyR__KbA4KxGFlDgoJ}YF zRygxn+}Xdm(z&1f1}=iFsLw;8PxR@m0(KA|zRJ1FzKSRT&sW1ZY&xORItLy5;T_Js z;Y(JmhS6B{lymptn2ur<{>qn>n1tQR&&(KLX&rzc_o?-|B3w`JeP*knZr9^BTTyJH zkQ~_vzS7Yp;Z~L24`e>nN}qoP?~m3qcLqY&Ka#omzTB;-L7Br4)S$GRi0MY7CxM8d z%$0~OBcBfxZ-GV9F-qowO0KMHT89cApV!Fa6Zv?IdxvTAP>V`3sIcc*!pHB#5D2ov z8@`{yUKk`W3sr)`_+rGX4&ByXE6BwTUMCA*I({=<5M>C4>`@dEi+U9yTrMdpQ67as zLu2l^gV%N6B{B1y&@6fVs=z1picHP7MhuqXk}lR$Y3G7qeOyG@q}6N_E{Hj z8N7ebet}bpk;u;o>3C-v-q^@YVgwS@54~4JWgMZ2U>)1rsF6Uq75luzJnLoH?y$iZ zvxLtmxU>2FeUBk1dg>8T1ZNVZ9*sdtPUjXeDrwzdD)I717BL?6r1u~elaD9AYgu}# zOwvLP%fr|09EU0nr{EKHT1AOn!<~Q%%0e6JsI1Gfq8(QcI};dl5W~^bbbi?uBEp!0 zN?AlAdqr01c3aGr)+{qaBk6P5iN#}A6_I24DVW?BhG3$671qyCno*Dl*HTEOQE3EC zrTWN?WdMv^GEP=Q6SP}HjS9Q?DVt&xIuLC{3ornRNNUSapDb&+Fh3_vdme@&D-bEL zD|)Ey=1vvT1n2t-w|*z5Qw2A7G`RX2?BiH)1A{?`?E05-d~DF!1s5ZocrTX=u9Dg@vZQ{FNbm1h85d{yETcGw)*4Fz2!FLC92`~Ef|P9mpl_= zV9VjJFUR@SDwip;fo&!X3Zl798}B^qvA+V}pT;d)x)Q%!8t6}D0>oPjLvX4tp0z(Z zkhzh*%r&9>`}+8WnJoT#Q-p!;=RyB z6qcjSA`aVhVrk)XNr}#a)CaVKGqb^6YCNi_zU&f}_$+$RW-#)+YTmJMyqk#dN*Y$$ zd!?nDysvWIlIrobE{5J4QYlztn4}3}I!8QtZ9)rHu-1C89baZ%V9j^=hvfK5qppZO z2we};>H-Y+vpF>a?gYL$hVyv}uh}nf4BH*UGopjqtP8C%QZJZ4Py0*gp{1Bh zAgM{ZYe92ZtOpFaip1_%QcNCq)og^r3yR6!H|#t!p&s|4=6Z+Lhmi15m}lM9;*mb0 zjCCO^sF&6`Y;b0#N0nO&E03+ZjPf`S+0srzZ&Vt>DAohYl-|NyjmeNyFw>atHe_e%(NUdjfu2{jt@ z{J#RJeDf#t5|YY#D~!$OR_ShEg1-Zdz#$l}5Bq0g4B+$JgXMhpDqVFPlsD_8SmA&C zF_nWg@F8#7?R@JI=htk-?U)K~#Vqsh9q-oAlZDs2nhG8se><;9!^h|lZZ1px@ctW} zOuBOme6Jk{{Y3a^sS!4N70`TjnaJgJ`j${9Fva7=L+WIj`_2qG;DwJz3(_g^iwI%N zi+Z2xMTN%20$9z|0R^{EB#;QauiBb(L0ks&=+#gNlrzl^3(GJI$6i!@FoHOC9TVZi zbN0*u=S*Q~Z9^M;GHW7>M#+?BaNXQ*H_ebKu}Ws0$f_`0m*6Zjthy8;v&!4nlu%n5 zhNLFILNJuR^M~+!LLPyI;sQn>pFyMstuWYL1aYIDb7zLktfSc0O8jm+wyz^91M3CZ zS{(bR@-f+DY31?n&E+o^5riJrOm-fyHRsPhneYuP)u-G`6`bxXI0>tHB`59d=>+ zmf#;fsL@RZJ;wD;pQ@$tV+jv9zhe=I0{4o~&yY9o^HxE_Z%$q%(<9u-fwQB6ci-&X z)rd7OJ){zHdepfOLV5qf7u0YZe?E)|(>}QKPcC!r=i}U!c_pp;ZWyGch~cMUP@0^s zhR@&m3g`a&SM_*0Ff6xYyGzeJU1Jda5p1LlKkglu353G`&{Hm~f)806asFT6Acqn7 zb~-F`LNh%DO*kw!Eb|>)xF&^W1m1eXyHV_a5D~#Fu4G9;#pP+0j0AGmC~|3x>d<%; zn%}2C!(4vH3Z&jVMSAg)V~m)4?<$Nnv~y8tnuda$Q(GgA)mZ8k*Ra9o)s5fA*Jc8nxx{YT{av+SvkS5-MI3 ztF8HnN|MQKp_}5A&#ZBd?`4LtD+;C86Q{;i9sJ(pPa*=tC9+2-m&m1swb_<>gQSyi zZyRJ=Z8u%kQR6UNEATCRZ>HlW<~nXk4TbY; zx!I=Fd&+CEz>k?b&rqa<6^hLXAP0H06(JsV|4M8pV14<=xI*dD>+Ybm~{&(lc{97qVI( z48XER#zcZ!U4v2|^b)czMHDax&vt-NNE~(x&;3;HHT8;>EQH7(4S93C-zgaZXz^6+ zLNLxAb38;~YsQ-i0V#6LyXQuh#C(39kb6nzuP9{w4Us%W=Br46#pGaduJu!kcV=lK zo&2TBSethWZCP{?g<-t_*7(z95%yXj6oyfn1@%JG`1~D3FjJdLX#?_b4 z3zO8uH{h)ek%=<&q+Q@8&EncMY68_Q=9ar!|7z$ND1Xl^>LpS%4q>%UY76yb5tj+w zwNJ43ei4P87KqDkx!!!NAMFkw3T1L!K6Ecn*Jhfrs8UJ%O~+^|@-EY@AeO{oX{cjS zdE|SxS^!+^0ndN~aBnE`kOMJ>XCjF3ol&0eM&Z zNgQNpB9{#AkyT>1t0;;jwPns$nM5D4aP8Hgu?Q_$vreHrUBT87u6YS#kcWPotuY{s zzBSyZ4pB=m5)DKL?Y!xH8%Ckigt@@y_|D|XjvJ{L**!-Jw{B_2&A|2#ZRofTl;OtZ z!98*?gmp6^`=}3chj~@=tShbaQ0|jZ?mItTA%fU_OpdjI<30UWZEY}n8KQ;tr}eYP z@%zs~_x|?nTHZWz19tmOc=l`c^$Ye}RDkXN8I=DIJ&d2hB{{>i6t2!VjL7ZS-|v3b zc5%6LAA!{FKI+^xN6%*rn&z|3nvb^!w_*kl?4;ZRb@^rIe&tce9KLt!o#*fc7z^Bj zQgQdgh-c=Z>;^jUeg|rjs~&(i-lp=Gf~9wD2yH*^UZ>Plj~51?OukEuKeweG3ZsKc zH^3OIkg3e40XEET4)(TgvufleB;P+dxx1Ay>QX>dM!lUo)0{L#+Z0OF7 z4&EnelBh7f74u6Q`cfg5y!Sn>%eL*oFTJX##pxk$SB7qlJ1o4k> zs-Ilq+%Mc|Mgr3Kj@z7j=|TA7hag0|G^I2IWAN^mIrlRh6lbg9_C4ZGyB9yG9J9x+ zbnY@l9=~;!j>lYwM+foieK1T9;4FT3uS#_-<53I=Gy7~N}X12kh4*XUc7rL zaQ1oEC{>h*$CuJ)=9Mxn7CD*s&Ro~i_AxC~tBSm$%M0OQGcUPX^o3UlsD!UR>xQh6 z^|PXAE7YsV)W~>Ku5kjG1qhPcl!(2SccX`@Kuh#TZnFaFk`l|L7+mib=3HqGog|Z7 zcd^MLv4J(BAi9&qj78LxWf$2boN__nteN*+giyt78e}S!EOzPmoKgyj5?*Y6SQJ$V z$Dqi^1OeTe*23e!rYfP#$yE#gWug?ycFon7j=&&{N-8J>h_v5<=Sw7>3}G4eH=-V~ zQ}k5k)A-Zb{jGTSUDA2$eX8s`i_lZ}Md}6P?(7kl?pqjxBH=WeOgfU&<}TPtZT1+a+AN+SQB#%^j7NzwcOT zsE`#K3Y&+)_3kzG$fCY>x!06!RA|s=4Q1ESXfY;84IJUSg|yPT(c|B-b2?sqf5li1E) zxMF|)F*Q6>_|E%q=bwlDZ#Wg3&b;|gBm`I~NdZ^BKmc5lBw#?F9hK#f2?*}%`0P^r zJ&T(_&}-hv-O`#D&KnZKMOI9z`C--qvRp?lhRljRPD#}msMe#BRxhWp!dZ6+xFt(E zS?KoMO7S+65>dAuw=gH3kxa8wY9Y;OdMK)k%p0_gIeeS#D+M&K*ygfkuVbz{*itCf z>&%ky*7~;eTBYQ-d#!-+OA-JUYtrW-=W%XoIFKAyN!Av?Pv(-~?NSoEd z0wbo;K}{N_ykCZAUW3!xkCBZhoy_fSGn@o-Y=|t3)GpLa>1IZTm|LV&TGSd6-m0t% zH;vabgxIJ?52%o?UP--51W?l@UsyWQDbJBar9q+Da&KsmNGi`r#5`q<5>yE%9+UGG zNxG%Bx*@Z5&l|F@FhW9Wo(x?&_F%FpD?{m!Y@LgKE@JVV&RE&C{SgjJ=ZA4kyEMYT zGGSU3WD*p6sm1J^>EnCX5MD2V;vR#7@5dQTB6k18E1mm|J9HKgCF`B|Yz?+? z^#S}2I{oBM=dS#ubN}OJ=U(+FHb)>hB8RUbDmf1K=2!Pw969SGp!Y24gUs4!_90Pd`-|i z77L0JtHlB9kv&lhxJ6bw2|m2E>%lH&=4i9n*p^#6 zV{RT*MHB70V7p#c1u2Y;NMfFhE2gv=1ai)?<5@bULlxGgu%*a!Dz$=^^1O(*3?=2q zrCH3AVmdnkS;y-J3`1kya5%3wjWuA<^_+E2$Su zB#OMf2$9|Sj6sB$&MbmP@;yc5Dv}{&RK}P3mC}3Q!^xgva?veNOPNVhNzR&FoH`AX z{#k`m=v9YgmO;0WW{OYKMO+YqtCu=WpE)Lx-lD%dOi46=v8|H+ zL1@XPfU8P2_83s1M&qEm*HTtiZ1zTNq5Ttix7n@Ry)4E}Ww-3s!a`MBc4>_ybTg4& zqI!)Wc+WeX2uZcI8#$hg>ZLSLh*S)VQvD+gqL-4 z%VZQ%Z)p>W#Da4MBaye|diUdX6H2YbS}W#C*J!j|QxHdpCsr1|4@P0hsLS>Zm2UNT z*mZEWlvVR0D1gr?cPpWuq>lLMuiLfYINlu)7Lv~GRooeQh4vsq_}#0VyBUu97MtlZ zHSFBS@!enh9Bv*Y_sMIV+Xm17>zr@nrOtg2QuVzs4g)i)HVQ~<4Sqo8 zxO_j4Q{0WqyAjX#;4$PB_Na5XR4p=u9A&P55SJxasO3Do!a?Tb6C?s1WVUzXw;O(f+0p;6HlvT z{{!>2jE+LqZ8)QME{$Q4e?C)%Us|6)W(7oFxAakFkkZTwVLlcMG8d0b-WTd!-|gg- zd47xp#ET&r*aSW~gYTaaP9Z526FMsMZQ)7h^RAq0yTMHzS6mX@^rNb9*S|`j=?nhN zKAFiJ=hiqI{UG7><*saQz`nQOS|WJOC`lWz-BBFNfEL(;rEz@d)(bTKTS^Lx@|AUw zUB05;maJLPRmQ;}iB+AzFDNQ=@|O4M;QQvcs1+<`5aRx36Qj^tg#XAbU>|knakGgo zl&p_zk_bVH$)V}mlO37;%k8%(TCQwOxV}|Er`}01zt5Ct&9ve`#V{MwAR(zF$0BcJ z{pcc*;y@&=DM)lBa!}JEvqU2bL)D0c5z2to7t|=|-?P?-(AHU#aSlW5A1Ixzg@o%b zILicB4WP6XsvLJ5p{g8NnWZ-+zA3d|ri^u@SiH7cK(4-@p@~#+D=JSGwi=I)IT})A zcH$Tc(4(-(F;tCj3MtomVJX)>Xe43LruPxKl}VPNhU?OaG~lJe*?pl!X(E+cFt+uq zBP5n(t&k?SvR>dA@IHL`cHR;PxQX{?RzOko6>iCl^N%8eK$PH4VgFmQ@Wu%rHaub# zsHO{Vy~_Fj{1u%Daq3n?2Ux9-;ZEGM*||UXcm*YW>n+Y*f84pPhn<_-0>NMF+~$ut z_h;Yj+-n|ENiNu6#`y|Z?XP~sY84!q!wzJ>gdG1a7hoJ!*Q6N$C-zU$Mb3J48Q8iG zA_Cu^iE_w0gok?Nfef+u5u_+AH@K#qw9%L1jqT)G(Cv7`VaWK?puGW>>rV5@g_8Yf zQJXaYd{=4{c)wmKs)*>JU|vJVQaW|P`&-(_TZ2m+1QTKzDoz2h5szL4LlHNYEA7Jm zs#J!C?wV(06p%}_P?x4R?EtMChfL-o@QB@?az1ecp&=h=fRw&kBaE!eLg~9b4zieh zZZlbf)L2RjAAF3Gj612@LMm*KrF3QruZwgHPvE@I~1M6}Brf}AM{ZMSfP~k^`MQwo;XW%HdFge=lE8(!XrZq8p)773KJN-sqPt~~h^_dsdRnhA<%fxmeH zy`(Y9h|Od17-|XKv`d=ABASV5mtje~I*(wzRNkVpn8CH3@1~2U9xB|(<#VnxOPb`| z`c3)`gwUu^M82;=wT03UrK$+4MlrYW+IC`OSg0~BHQJFPuB1DRKAg`IMHq^W^@Sov zWHpsqtSY9Ml)MtKp3 zcy@}=g`Y}4Wzl6ss?c_E)MpX8(PoG+>jc($6#x>R{ttJ^$Q%M1z8IUX{ z*_~uUvdApksks~mA+JICw>pB(s`1e(+<>pSL2P>(pVw(T`a5nKYi(|}aCx(ITUG@G z=jDcD?a~e4c(+PFdwBg*uH1t-vbD~IQJ=%X(0Q!ib@rsSvf*kCxxn z+FA=>F1%)3nO0(y*JW;){@C4X+EpUFpbcHobMz5G6ltKdU`;KVi(@`2YTwGUpfO=z zL%}APEH}1;Oen7f7RtdjgaS(O%|h$gq>~xa$T~B02#kf*QndEwA=&pETF4SGP)$=K z63%8dli_qBD1AZJZ7z%S(w2J@$eec3l0lSSvapM0r-fb9=Nw)>21l>y8f#{u;JFA% z>V^BVtWt7kbFx64&u4D@8@cOW-w(w$f@9?@+)c_eltX;kRFpBWF zh?)6NjOy6bSi^D^Wpu?N>bt2|^(73RYI87(5W9}scFj66s*?D7DJ~P;6>_T|ehI21T39I&BcmjgwGbT9@xjzeJ+AO;h#Lu@Jp8!UtY z0YYMu(1;k+l3J}+cfWq~bfC>JXDb_;hOFWx(!YQ@jyo@aWT-& zvlBh`|71G*McCJndA`p1Zriwf0(X)5b|_#6^tnOAYSCQpGSu8R&jRk!K}-I;(5Bq2dNK?H6DC&5QY z-i1KlNRtoP{bde*!W`XKP_Fc$53(IAd0~0Xr&p57gu|V*ik(NHvE+O*F|yMj0^ zUr6*^ehQw?fxmF&|AG67ew$DI|MUk`4PHg9^zHn!?=f6EfBN*~aan+u=PRHDD~c6{ z8%xjfon&AO_$y3zlYXDlsjU0rvoqH*krBWwn!}bdIHsJFiNHn&{+?dvKxo~9NuYp= z-zBRoXCR14pq9zS8X7zStz#OCHy||YESqN^{{R^znLl?}S^^EE>*6|*awwxbHpT-` zF9UD_C^~Ux!ibHSB}W&6v+6?UmQ_o@WTI1*#|A1$g8PU{9Pig5g(h`fYBdxgcBdm1 zayv>xIz+t^90>dC{Oe@kcTuZRk(7x2Ej0`2ww5&BT+#9pSEjGF(aC91x_WgBg|nkX zAN)acdlK`HZZpu8H+6LMrQ8BoF%ShH{sO4-tpnaOou;#-I7ym61V?;F(8 zaek8Tyg5c#0`=|1(s5?*jh{S=mosz%#|-x7jR6!ueNs0AOSw71U=CO+qx!F7B-sN1{bSJB zP|5RD@AYX{7}0D5El;)L)ZVG z>}fc-Nac;6LG;6KvOE74Cx-uzQ}|0CBYF|o&vN(|`SZVm^xQr8HN9Y5dq=lx&hHIr ztXW5L`zAVseZ4J%E_`PjL7V4!gRbbrLN1-m?IlC5=i)>b$nhW@PsX5bozAxmTB@ zA~vV0qhSV1q5@IAaEIz>gO8#Ghm1$4&Si5GA=8^5#P%f00$zWL$$+micfNVW_ry$mt&bU*Og$J*;;wH5!$5bwr*a4WKi2D{rBb=g zx5qwTqcsgTxQko!-Lzfk)_eJ0=3;w(0z|`<>U%bM@|n%|bF9l*Lhx0M9QQr|KGO(m zZB&#i!P|x5CV^U{EDFYp1q=GBe}rxW+FvGf0ic9Rr@*Bz`K&)RIn;#Y59`b@TELc% z)$EB}$rQvgR{+2mNe(IIMn0s}YJ{3`F%D7>aS+d%ECrB***c=mW!#;p3Q@j@e-j|C zZcaGjXYz!|=Hyg)%?>*YEmTErCK>Whm)W7z;wNEu(Lq5(3q|QdWIQfFm}Fg3myB;U zatAJo3tP#6C*2~_z#oh3g{TuB^0ip-_ex}11c7Ws1uyf*3;y@w4=LZ64(;Q87 zR4VdgLEA2=_PpXR4WX~y#hSlwLy$+orh?YmTT3s97yXUE&9C(r1Z|~sGrCN)E_2T3lnW8ew<3?;WkU!;Xf6eCp16*lSn4cP~P(57*&14++q&O8z- zYjS0Ra%84568wV_$M{z0F7xqR;b3-~X2;EAc6Hh2)Zzdo2H$bkI7B~xAJJn!VF^K! zgCvFHFAKDMtkX($c4V7_#z~cH7BDg?%U1p?sG*bm?`=y26^cF$lCip7)wk-XAuP+4 z!5eHg9SrWp=V5KoMjGp0WXgJWrg-oXQ4xs41-=KK;IBQZya_`Stv^LH5tmkv@_l@! zGs{rgG1#nhJ|eX(lUiLOyS#asIF{u$GcOa-zF<&Y+dmv=mlkJhPS7J?pF+R-@~-qCrz;^m&9nSgP_VKHg0HLZb7PRQe)W#L?g9_ORCd zYLfHaM+w-I`6Afg0}8y-x@xP|>nsXSn49w0__-%jOzqH~bh?{>?hzKy{lsN3LQ1

c!kmnT3fWq}n8 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

IVoQLb$(XwH@NKb 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 IM6N<$f>L~g`~Uy| literal 0 HcmV?d00001 diff --git a/examples/example 15 - Filters/LightRotate2.png b/examples/example 15 - Filters/LightRotate2.png new file mode 100644 index 0000000000000000000000000000000000000000..a6e63c01b721611585c5c0337fb06b0714d04d92 GIT binary patch literal 173189 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%0D(tFu%iSmUvCHaDcCRA! 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

F_Ld-6A#TJ9MBb=+JbjW65q{B>VenMagW~ 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

D* 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%0DH{%cI44m`nP!5L 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 z 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 + + + pixi.js example 15 - Filters + + + + + + + + + diff --git a/examples/example 15 - Filters/panda.png b/examples/example 15 - Filters/panda.png new file mode 100644 index 0000000000000000000000000000000000000000..215a4a9331fbe6bb3efe9501b672df8e5d8be76e GIT binary patch literal 69177 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 x1 && x <= x1 + this.width) + { + var y1 = this.y; + + if(y >= y1 && y <= y1 + this.height) + { + return true; + } + } + + return false; +} + +// constructor +PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; + + +/** + * @author Adrien Brault + */ + +/** + * @class Polygon + * @constructor + * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be + * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the + * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are + * Numbers. + */ +PIXI.Polygon = function(points) +{ + //if points isn't an array, use arguments as the array + if(!(points instanceof Array)) + points = Array.prototype.slice.call(arguments); + + //if this is a flat array of numbers, convert it to points + if(typeof points[0] === 'number') { + var p = []; + for(var i = 0, il = points.length; i < il; i+=2) { + p.push( + new PIXI.Point(points[i], points[i + 1]) + ); + } + + points = p; + } + + this.points = points; +} + +/** + * Creates a clone of this polygon + * + * @method clone + * @return {Polygon} a copy of the polygon + */ +PIXI.Polygon.prototype.clone = function() +{ + var points = []; + for (var i=0; i y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); + + if(intersect) inside = !inside; + } + + return inside; +} + +// constructor +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + +/** + * @author Chad Engler + */ + +/** + * 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 radius {Number} The radius of the circle + */ +PIXI.Circle = function(x, y, radius) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +} + +/** + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon + */ +PIXI.Circle.prototype.clone = function() +{ + return new PIXI.Circle(this.x, this.y, this.radius); +} + +/** + * Checks if the x, and y coords 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 + */ +PIXI.Circle.prototype.contains = function(x, y) +{ + if(this.radius <= 0) + return false; + + var dx = (this.x - x), + dy = (this.y - y), + r2 = this.radius * this.radius; + + dx *= dx; + dy *= dy; + + return (dx + dy <= r2); +} + +// constructor +PIXI.Circle.prototype.constructor = PIXI.Circle; + + +/** + * @author Chad Engler + */ + +/** + * 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 width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse + */ +PIXI.Ellipse = function(x, y, width, height) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property width + * @type Number + * @default 0 + */ + this.width = width || 0; + + /** + * @property height + * @type Number + * @default 0 + */ + this.height = height || 0; +} + +/** + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse + */ +PIXI.Ellipse.prototype.clone = function() +{ + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); +} + +/** + * Checks if the x, and y coords 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 + */ +PIXI.Ellipse.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; + + //normalize the coords to an ellipse with center 0,0 + //and a radius of 0.5 + var normx = ((x - this.x) / this.width) - 0.5, + normy = ((y - this.y) / this.height) - 0.5; + + normx *= normx; + normy *= normy; + + return (normx + normy < 0.25); +} + +PIXI.Ellipse.getBounds = function() +{ + return new PIXI.Rectangle(this.x, this.y, this.width, this.height); +} + +// constructor +PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; + + + +/* + * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV + * you both rock! + */ + +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; +} + + +PIXI.mat3.identity = function(matrix) +{ + 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; +} + + +PIXI.mat4 = {}; + +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.mat3.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], + 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) + { + 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; + } + + 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; +} + +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; +} + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The base class for all objects that are rendered on the screen. + * + * @class DisplayObject + * @constructor + */ +PIXI.DisplayObject = function() +{ + this.last = this; + this.first = this; + /** + * The coordinate of the object relative to the local coordinates of the parent. + * + * @property position + * @type Point + */ + this.position = new PIXI.Point(); + + /** + * The scale factor of the object. + * + * @property scale + * @type Point + */ + this.scale = new PIXI.Point(1,1);//{x:1, y:1}; + + /** + * The pivot point of the displayObject that it rotates around + * + * @property pivot + * @type Point + */ + this.pivot = new PIXI.Point(0,0); + + /** + * The rotation of the object in radians. + * + * @property rotation + * @type Number + */ + this.rotation = 0; + + /** + * The opacity of the object. + * + * @property alpha + * @type Number + */ + this.alpha = 1; + + /** + * The visibility of the object. + * + * @property visible + * @type Boolean + */ + this.visible = true; + + /** + * This is the defined area that will pick up mouse / touch events. It is null by default. + * Setting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children) + * + * @property hitArea + * @type Rectangle|Circle|Ellipse|Polygon + */ + this.hitArea = null; + + /** + * This is used to indicate if the displayObject should display a mouse hand cursor on rollover + * + * @property buttonMode + * @type Boolean + */ + this.buttonMode = false; + + /** + * Can this object be rendered + * + * @property renderable + * @type Boolean + */ + this.renderable = false; + + /** + * [read-only] The display object container that contains this display object. + * + * @property parent + * @type DisplayObjectContainer + * @readOnly + */ + this.parent = null; + + /** + * [read-only] The stage the display object is connected to, or undefined if it is not connected to the stage. + * + * @property stage + * @type Stage + * @readOnly + */ + this.stage = null; + + /** + * [read-only] The multiplied alpha of the displayobject + * + * @property worldAlpha + * @type Number + * @readOnly + */ + this.worldAlpha = 1; + + /** + * [read-only] Whether or not the object is interactive, do not toggle directly! use the `interactive` property + * + * @property _interactive + * @type Boolean + * @readOnly + * @private + */ + this._interactive = false; + + /** + * [read-only] Current transform of the object based on world (parent) factors + * + * @property worldTransform + * @type Mat3 + * @readOnly + * @private + */ + this.worldTransform = PIXI.mat3.create()//mat3.identity(); + + /** + * [read-only] Current transform of the object locally + * + * @property localTransform + * @type Mat3 + * @readOnly + * @private + */ + this.localTransform = PIXI.mat3.create()//mat3.identity(); + + /** + * [NYI] Unkown + * + * @property color + * @type Array<> + * @private + */ + this.color = []; + + /** + * [NYI] Holds whether or not this object is dynamic, for rendering optimization + * + * @property dynamic + * @type Boolean + * @private + */ + this.dynamic = true; + + // chach that puppy! + this._sr = 0; + this._cr = 1; + + /* + * MOUSE Callbacks + */ + + /** + * A callback that is used when the users clicks on the displayObject with their mouse + * @method click + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user clicks the mouse down over the sprite + * @method mousedown + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse that was over the displayObject + * for this callback to be fired the mouse must have been pressed down over the displayObject + * @method mouseup + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse that was over the displayObject but is no longer over the displayObject + * for this callback to be fired, The touch must have started over the displayObject + * @method mouseupoutside + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the users mouse rolls over the displayObject + * @method mouseover + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the users mouse leaves the displayObject + * @method mouseout + * @param interactionData {InteractionData} + */ + + + /* + * TOUCH Callbacks + */ + + /** + * A callback that is used when the users taps on the sprite with their finger + * basically a touch version of click + * @method tap + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user touch's over the displayObject + * @method touchstart + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases a touch over the displayObject + * @method touchend + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the touch that was over the displayObject + * for this callback to be fired, The touch must have started over the sprite + * @method touchendoutside + * @param interactionData {InteractionData} + */ +} + +// constructor +PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; + +/** + * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default + * Instead of using this function you can now simply set the interactive property to true or false + * + * @method setInteractive + * @param interactive {Boolean} + * @deprecated Simply set the `interactive` property directly + */ +PIXI.DisplayObject.prototype.setInteractive = function(interactive) +{ + this.interactive = interactive; +} + +/** + * Indicates if the sprite will have touch and mouse interactivity. It is false by default + * + * @property interactive + * @type Boolean + * @default false + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', { + get: function() { + return this._interactive; + }, + set: function(value) { + this._interactive = value; + + // TODO more to be done here.. + // need to sort out a re-crawl! + if(this.stage)this.stage.dirty = true; + } +}); + +/** + * 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. + * To remove a mask, set this property to null. + * + * @property mask + * @type Graphics + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { + get: function() { + return this._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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); + this.addFilter(value) + } + else + { + if(this._filters)this.removeFilter(this._filters); + } + + 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; + + // 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; + + /* + * 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 + */ + var childFirst = end + var childLast = end + var nextObject = null; + var 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; + + var nextObject = lastBlock._iNext; + var 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 + * + * @method updateTransform + * @private + */ +PIXI.DisplayObject.prototype.updateTransform = function() +{ + // TODO OPTIMIZE THIS!! with dirty + if(this.rotation !== this.rotationCache) + { + 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; + //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], + + b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], + b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; + + localTransform[2] = a02 + localTransform[5] = a12 + + 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; +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * A DisplayObjectContainer represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class DisplayObjectContainer + * @extends DisplayObject + * @constructor + */ +PIXI.DisplayObjectContainer = function() +{ + PIXI.DisplayObject.call( this ); + + /** + * [read-only] The of children of this container. + * + * @property children + * @type Array + * @readOnly + */ + this.children = []; +} + +// constructor +PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); +PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; + +/** + * Adds a child to the container. + * + * @method addChild + * @param child {DisplayObject} The DisplayObject to add to the container + */ +PIXI.DisplayObjectContainer.prototype.addChild = function(child) +{ + if(child.parent != undefined) + { + + //// 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) + { + 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); + } + +} + +/** + * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown + * + * @method addChildAt + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + */ +PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) +{ + if(index >= 0 && index <= this.children.length) + { + if(child.parent != undefined) + { + 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); + } + + } + else + { + throw new Error(child + " The index "+ index +" supplied is out of bounds " + this.children.length); + } +} + +/** + * [NYI] Swaps the depth of 2 displayObjects + * + * @method swapChildren + * @param child {DisplayObject} + * @param child2 {DisplayObject} + * @private + */ +PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) +{ + /* + * this funtion needs to be recoded.. + * can be done a lot faster.. + */ + return; + + // need to fix this function :/ + /* + // TODO I already know this?? + var index = this.children.indexOf( child ); + var index2 = this.children.indexOf( child2 ); + + if ( index !== -1 && index2 !== -1 ) + { + // cool + + /* + if(this.stage) + { + // this is to satisfy the webGL batching.. + // TODO sure there is a nicer way to achieve this! + this.stage.__removeChild(child); + this.stage.__removeChild(child2); + + this.stage.__addChild(child); + this.stage.__addChild(child2); + } + + // swap the positions.. + this.children[index] = child2; + this.children[index2] = child; + + } + else + { + throw new Error(child + " Both the supplied DisplayObjects must be a child of the caller " + this); + }*/ +} + +/** + * Returns the Child at the specified index + * + * @method getChildAt + * @param index {Number} The index to get the child from + */ +PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) +{ + if(index >= 0 && index < this.children.length) + { + return this.children[index]; + } + else + { + throw new Error(child + " Both the supplied DisplayObjects must be a child of the caller " + this); + } +} + +/** + * Removes a child from the container. + * + * @method removeChild + * @param child {DisplayObject} The DisplayObject to remove + */ +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.last) + { + 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); + } + + child.parent = undefined; + this.children.splice( index, 1 ); + } + else + { + throw new Error(child + " The supplied DisplayObject must be a child of the caller " + this); + } +} + +/* + * Updates the container's children's transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.DisplayObjectContainer.prototype.updateTransform = function() +{ + if(!this.visible)return; + + PIXI.DisplayObject.prototype.updateTransform.call( this ); + + for(var i=0,j=this.children.length; i} an array of {Texture} objects that make up the animation + */ +PIXI.MovieClip = function(textures) +{ + PIXI.Sprite.call(this, textures[0]); + + /** + * The array of textures that make up the animation + * + * @property textures + * @type Array + */ + this.textures = textures; + + /** + * The speed that the MovieClip will play at. Higher is faster, lower is slower + * + * @property animationSpeed + * @type Number + * @default 1 + */ + this.animationSpeed = 1; + + /** + * Whether or not the movie clip repeats after playing. + * + * @property loop + * @type Boolean + * @default true + */ + this.loop = true; + + /** + * Function to call when a MovieClip finishes playing + * + * @property onComplete + * @type Function + */ + this.onComplete = null; + + /** + * [read-only] The index MovieClips current frame (this may not have to be a whole number) + * + * @property currentFrame + * @type Number + * @default 0 + * @readOnly + */ + this.currentFrame = 0; + + /** + * [read-only] Indicates if the MovieClip is currently playing + * + * @property playing + * @type Boolean + * @readOnly + */ + this.playing = false; +} + +// constructor +PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); +PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; + +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + +/** + * Stops the MovieClip + * + * @method stop + */ +PIXI.MovieClip.prototype.stop = function() +{ + this.playing = false; +} + +/** + * Plays the MovieClip + * + * @method play + */ +PIXI.MovieClip.prototype.play = function() +{ + this.playing = true; +} + +/** + * Stops the MovieClip and goes to a specific frame + * + * @method gotoAndStop + * @param frameNumber {Number} frame index to stop at + */ +PIXI.MovieClip.prototype.gotoAndStop = function(frameNumber) +{ + this.playing = false; + this.currentFrame = frameNumber; + var round = (this.currentFrame + 0.5) | 0; + this.setTexture(this.textures[round % this.textures.length]); +} + +/** + * Goes to a specific frame and begins playing the MovieClip + * + * @method gotoAndPlay + * @param frameNumber {Number} frame index to start at + */ +PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) +{ + this.currentFrame = frameNumber; + this.playing = true; +} + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.MovieClip.prototype.updateTransform = function() +{ + PIXI.Sprite.prototype.updateTransform.call(this); + + if(!this.playing)return; + + this.currentFrame += this.animationSpeed; + + var round = (this.currentFrame + 0.5) | 0; + + if(this.loop || round < this.textures.length) + { + this.setTexture(this.textures[round % this.textures.length]); + } + else if(round >= this.textures.length) + { + this.gotoAndStop(this.textures.length - 1); + if(this.onComplete) + { + this.onComplete(); + } + } +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + + +PIXI.FilterBlock = function() +{ + this.visible = true; + this.renderable = true; +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} + + +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Text Object will create a line(s) of text to split a line you can use "\n" + * + * @class Text + * @extends Sprite + * @constructor + * @param text {String} The copy that you would like the text to display + * @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.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 + * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap + */ +PIXI.Text = function(text, style) +{ + this.canvas = document.createElement("canvas"); + this.context = this.canvas.getContext("2d"); + PIXI.Sprite.call(this, PIXI.Texture.fromCanvas(this.canvas)); + + this.setText(text); + this.setStyle(style); + + this.updateText(); + this.dirty = false; +}; + +// constructor +PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); +PIXI.Text.prototype.constructor = PIXI.Text; + +/** + * Set the style of the text + * + * @method setStyle + * @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.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 + * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap + */ +PIXI.Text.prototype.setStyle = function(style) +{ + style = style || {}; + style.font = style.font || "bold 20pt Arial"; + style.fill = style.fill || "black"; + style.align = style.align || "left"; + style.stroke = style.stroke || "black"; //provide a default, see: https://github.com/GoodBoyDigital/pixi.js/issues/136 + style.strokeThickness = style.strokeThickness || 0; + style.wordWrap = style.wordWrap || false; + style.wordWrapWidth = style.wordWrapWidth || 100; + this.style = style; + this.dirty = true; +}; + +/** + * Set the copy for the text object. To split a line you can use "\n" + * + * @methos setText + * @param {String} text The copy that you would like the text to display + */ +PIXI.Text.prototype.setText = function(text) +{ + this.text = text.toString() || " "; + this.dirty = true; +}; + +/** + * Renders text + * + * @method updateText + * @private + */ +PIXI.Text.prototype.updateText = function() +{ + this.context.font = this.style.font; + + var outputText = this.text; + + // word wrap + // preserve original text + if(this.style.wordWrap)outputText = this.wordWrap(this.text); + + //split text into lines + var lines = outputText.split(/(?:\r\n|\r|\n)/); + + //calculate text width + var lineWidths = []; + var maxLineWidth = 0; + for (var i = 0; i < lines.length; i++) + { + var lineWidth = this.context.measureText(lines[i]).width; + lineWidths[i] = lineWidth; + maxLineWidth = Math.max(maxLineWidth, lineWidth); + } + this.canvas.width = maxLineWidth + this.style.strokeThickness; + + //calculate text height + var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; + this.canvas.height = lineHeight * lines.length; + + //set canvas text styles + this.context.fillStyle = this.style.fill; + this.context.font = this.style.font; + + this.context.strokeStyle = this.style.stroke; + this.context.lineWidth = this.style.strokeThickness; + + this.context.textBaseline = "top"; + + //draw lines line by line + for (i = 0; i < lines.length; i++) + { + var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); + + if(this.style.align == "right") + { + linePosition.x += maxLineWidth - lineWidths[i]; + } + else if(this.style.align == "center") + { + linePosition.x += (maxLineWidth - lineWidths[i]) / 2; + } + + if(this.style.stroke && this.style.strokeThickness) + { + this.context.strokeText(lines[i], linePosition.x, linePosition.y); + } + + if(this.style.fill) + { + this.context.fillText(lines[i], linePosition.x, linePosition.y); + } + } + + this.updateTexture(); +}; + +/** + * Updates texture size based on canvas size + * + * @method updateTexture + * @private + */ +PIXI.Text.prototype.updateTexture = function() +{ + this.texture.baseTexture.width = this.canvas.width; + this.texture.baseTexture.height = this.canvas.height; + this.texture.frame.width = this.canvas.width; + this.texture.frame.height = this.canvas.height; + + this._width = this.canvas.width; + this._height = this.canvas.height; + + PIXI.texturesToUpdate.push(this.texture.baseTexture); +}; + +/** + * Updates the transfor of this object + * + * @method updateTransform + * @private + */ +PIXI.Text.prototype.updateTransform = function() +{ + if(this.dirty) + { + this.updateText(); + this.dirty = false; + } + + PIXI.Sprite.prototype.updateTransform.call(this); +}; + +/* + * http://stackoverflow.com/users/34441/ellisbben + * great solution to the problem! + * + * @method determineFontHeight + * @param fontStyle {Object} + * @private + */ +PIXI.Text.prototype.determineFontHeight = function(fontStyle) +{ + // build a little reference dictionary so if the font style has been used return a + // cached version... + var result = PIXI.Text.heightCache[fontStyle]; + + if(!result) + { + var body = document.getElementsByTagName("body")[0]; + var dummy = document.createElement("div"); + var dummyText = document.createTextNode("M"); + dummy.appendChild(dummyText); + dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); + body.appendChild(dummy); + + result = dummy.offsetHeight; + PIXI.Text.heightCache[fontStyle] = result; + + body.removeChild(dummy); + } + + return result; +}; + +/** + * A Text Object will apply wordwrap + * + * @method wordWrap + * @param text {String} + * @private + */ +PIXI.Text.prototype.wordWrap = function(text) +{ + // search good wrap position + var searchWrapPos = function(ctx, text, start, end, wrapWidth) + { + var p = Math.floor((end-start) / 2) + start; + if(p == start) { + return 1; + } + + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) + { + if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) + { + return p; + } + else + { + return arguments.callee(ctx, text, p, end, wrapWidth); + } + } + else + { + return arguments.callee(ctx, text, start, p, wrapWidth); + } + }; + + var lineWrap = function(ctx, text, wrapWidth) + { + if(ctx.measureText(text).width <= wrapWidth || text.length < 1) + { + return text; + } + var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); + return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); + }; + + var result = ""; + var lines = text.split("\n"); + for (var i = 0; i < lines.length; i++) + { + result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; + } + + return result; +}; + +/** + * Destroys this text object + * + * @method destroy + * @param destroyTexture {Boolean} + */ +PIXI.Text.prototype.destroy = function(destroyTexture) +{ + if(destroyTexture) + { + this.texture.destroy(); + } + +}; + +PIXI.Text.heightCache = {}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" + * You can generate the fnt files using + * http://www.angelcode.com/products/bmfont/ for windows or + * http://www.bmglyph.com/ for mac. + * + * @class BitmapText + * @extends DisplayObjectContainer + * @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") + */ +PIXI.BitmapText = function(text, style) +{ + PIXI.DisplayObjectContainer.call(this); + + this.setText(text); + this.setStyle(style); + this.updateText(); + this.dirty = false + +}; + +// constructor +PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +PIXI.BitmapText.prototype.constructor = PIXI.BitmapText; + +/** + * Set the copy for the text object + * + * @method setText + * @param text {String} The copy that you would like the text to display + */ +PIXI.BitmapText.prototype.setText = function(text) +{ + this.text = text || " "; + this.dirty = true; +}; + +/** + * Set the style of the 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") + */ +PIXI.BitmapText.prototype.setStyle = function(style) +{ + style = style || {}; + style.align = style.align || "left"; + this.style = style; + + var font = style.font.split(" "); + this.fontName = font[font.length - 1]; + this.fontSize = font.length >= 2 ? parseInt(font[font.length - 2], 10) : PIXI.BitmapText.fonts[this.fontName].size; + + this.dirty = true; +}; + +/** + * Renders text + * + * @method updateText + * @private + */ +PIXI.BitmapText.prototype.updateText = function() +{ + var data = PIXI.BitmapText.fonts[this.fontName]; + var pos = new PIXI.Point(); + var prevCharCode = null; + var chars = []; + var maxLineWidth = 0; + 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); + if(/(?:\r\n|\r|\n)/.test(this.text.charAt(i))) + { + lineWidths.push(pos.x); + maxLineWidth = Math.max(maxLineWidth, pos.x); + line++; + + pos.x = 0; + pos.y += data.lineHeight; + prevCharCode = null; + continue; + } + + var charData = data.chars[charCode]; + if(!charData) continue; + + if(prevCharCode && charData[prevCharCode]) + { + pos.x += charData.kerning[prevCharCode]; + } + chars.push({texture:charData.texture, line: line, charCode: charCode, position: new PIXI.Point(pos.x + charData.xOffset, pos.y + charData.yOffset)}); + pos.x += charData.xAdvance; + + prevCharCode = charCode; + } + + lineWidths.push(pos.x); + maxLineWidth = Math.max(maxLineWidth, pos.x); + + var lineAlignOffsets = []; + for(i = 0; i <= line; i++) + { + var alignOffset = 0; + if(this.style.align == "right") + { + alignOffset = maxLineWidth - lineWidths[i]; + } + else if(this.style.align == "center") + { + alignOffset = (maxLineWidth - lineWidths[i]) / 2; + } + lineAlignOffsets.push(alignOffset); + } + + for(i = 0; i < chars.length; i++) + { + var c = new PIXI.Sprite(chars[i].texture)//PIXI.Sprite.fromFrame(chars[i].charCode); + 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); + } + + this.width = pos.x * scale; + this.height = (pos.y + data.lineHeight) * scale; +}; + +/** + * Updates the transfor of this object + * + * @method updateTransform + * @private + */ +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.BitmapText.fonts = {}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + /** + * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive + * This manager also supports multitouch. + * + * @class InteractionManager + * @constructor + * @param stage {Stage} The stage to handle interactions + */ +PIXI.InteractionManager = function(stage) +{ + /** + * a refference to the stage + * + * @property stage + * @type Stage + */ + this.stage = stage; + + /** + * the mouse data + * + * @property mouse + * @type InteractionData + */ + this.mouse = new PIXI.InteractionData(); + + /** + * an object that stores current touches (InteractionData) by id reference + * + * @property touchs + * @type Object + */ + this.touchs = {}; + + + + // helpers + this.tempPoint = new PIXI.Point(); + //this.tempMatrix = mat3.create(); + + this.mouseoverEnabled = true; + + //tiny little interactiveData pool! + this.pool = []; + + this.interactiveItems = []; + this.interactionDOMElement = null; + + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + + this.last = 0; +} + +// constructor +PIXI.InteractionManager.prototype.constructor = PIXI.InteractionManager; + +/** + * Collects an interactive sprite recursively to have their interactions managed + * + * @method collectInteractiveSprite + * @param displayObject {DisplayObject} the displayObject to collect + * @param iParent {DisplayObject} + * @private + */ +PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) +{ + var children = displayObject.children; + var length = children.length; + + /// make an interaction tree... {item.__interactiveParent} + for (var i = length-1; i >= 0; i--) + { + var child = children[i]; + +// if(child.visible) { + // push all interactive bits + if(child.interactive) + { + iParent.interactiveChildren = true; + //child.__iParent = iParent; + this.interactiveItems.push(child); + + if(child.children.length > 0) + { + this.collectInteractiveSprite(child, child); + } + } + else + { + child.__iParent = null; + + if(child.children.length > 0) + { + this.collectInteractiveSprite(child, iParent); + } + } +// } + } +} + +/** + * Sets the target for event delegation + * + * @method setTarget + * @param target {WebGLRenderer|CanvasRenderer} the renderer to bind events to + * @private + */ +PIXI.InteractionManager.prototype.setTarget = function(target) +{ + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + + if (window.navigator.msPointerEnabled) + { + // time to remove some of that zoom in ja.. + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; + + // DO some window specific touch! + } + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); +} + + +/** + * updates the state of interactive objects + * + * @method update + * @private + */ +PIXI.InteractionManager.prototype.update = function() +{ + if(!this.target)return; + + // frequency of 30fps?? + var now = Date.now(); + var diff = now - this.last; + diff = (diff * 30) / 1000; + if(diff < 1)return; + this.last = now; + // + + // ok.. so mouse events?? + // yes for now :) + // OPTIMSE - how often to check?? + if(this.dirty) + { + this.dirty = false; + + var len = this.interactiveItems.length; + + for (var i=0; i < len; i++) { + this.interactiveItems[i].interactiveChildren = false; + } + + this.interactiveItems = []; + + if(this.stage.interactive)this.interactiveItems.push(this.stage); + // go through and collect all the objects that are interactive.. + this.collectInteractiveSprite(this.stage, this.stage); + } + + // loop through interactive objects! + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + + //if(!item.visible)continue; + + // 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) + { + // 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) this.interactionDOMElement.style.cursor = "pointer"; + + if(!item.__isOver) + { + + if(item.mouseover)item.mouseover(this.mouse); + item.__isOver = true; + } + } + else + { + if(item.__isOver) + { + // roll out! + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } + } + + // ---> + } +} + +/** + * Is called when the mouse moves accross the renderer element + * + * @method onMouseMove + * @param event {Event} The DOM event of the mouse moving + * @private + */ +PIXI.InteractionManager.prototype.onMouseMove = function(event) +{ + this.mouse.originalEvent = event || window.event; //IE uses window.event + // TODO optimize by not check EVERY TIME! maybe half as often? // + var rect = this.interactionDOMElement.getBoundingClientRect(); + + this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); + this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); + + var length = this.interactiveItems.length; + var global = this.mouse.global; + + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.mousemove) + { + //call the function! + item.mousemove(this.mouse); + } + } +} + +/** + * Is called when the mouse button is pressed down on the renderer element + * + * @method onMouseDown + * @param event {Event} The DOM event of a mouse button being pressed down + * @private + */ +PIXI.InteractionManager.prototype.onMouseDown = function(event) +{ + this.mouse.originalEvent = event || window.event; //IE uses window.event + + // loop through inteaction tree... + // hit test each item! -> + // get interactive items under point?? + //stage.__i + var length = this.interactiveItems.length; + var global = this.mouse.global; + + var index = 0; + var parent = this.stage; + + // while + // hit test + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.mousedown || item.click) + { + item.__mouseIsDown = true; + item.__hit = this.hitTest(item, this.mouse); + + if(item.__hit) + { + //call the function! + if(item.mousedown)item.mousedown(this.mouse); + item.__isDown = true; + + // just the one! + if(!item.interactiveChildren)break; + } + } + } +} + + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + +/** + * Is called when the mouse button is released on the renderer element + * + * @method onMouseUp + * @param event {Event} The DOM event of a mouse button being released + * @private + */ +PIXI.InteractionManager.prototype.onMouseUp = function(event) +{ + this.mouse.originalEvent = event || window.event; //IE uses window.event + + var global = this.mouse.global; + + + var length = this.interactiveItems.length; + var up = false; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.mouseup || item.mouseupoutside || item.click) + { + item.__hit = this.hitTest(item, this.mouse); + + if(item.__hit && !up) + { + //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; + } + else + { + if(item.__isDown) + { + if(item.mouseupoutside)item.mouseupoutside(this.mouse); + } + } + + item.__isDown = false; + } + } +} + +/** + * Tests if the current mouse coords 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 + * @private + */ +PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) +{ + var global = interactionData.global; + + if(item.vcount !== PIXI.visibleCount)return false; + + 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], + 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; + + interactionData.target = item; + + //a sprite or display object with a hit area defined + if(item.hitArea && item.hitArea.contains) { + if(item.hitArea.contains(x, y)) { + //if(isSprite) + interactionData.target = item; + + return true; + } + + return false; + } + // a sprite with no hitarea defined + else if(isSprite) + { + var width = item.texture.frame.width, + height = item.texture.frame.height, + x1 = -width * item.anchor.x, + y1; + + if(x > x1 && x < x1 + width) + { + y1 = -height * item.anchor.y; + + if(y > y1 && y < y1 + height) + { + // set the target property if a hit is true! + interactionData.target = item + return true; + } + } + } + + var length = item.children.length; + + for (var i = 0; i < length; i++) + { + var tempItem = item.children[i]; + var hit = this.hitTest(tempItem, interactionData); + if(hit) + { + // hmm.. TODO SET CORRECT TARGET? + interactionData.target = item + return true; + } + } + + return false; +} + +/** + * Is called when a touch is moved accross the renderer element + * + * @method onTouchMove + * @param event {Event} The DOM event of a touch moving accross the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchMove = function(event) +{ + var rect = this.interactionDOMElement.getBoundingClientRect(); + var changedTouches = event.changedTouches; + + for (var i=0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.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); + } + + var length = this.interactiveItems.length; + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + if(item.touchmove)item.touchmove(touchData); + } +} + +/** + * Is called when a touch is started on the renderer element + * + * @method onTouchStart + * @param event {Event} The DOM event of a touch starting on the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchStart = function(event) +{ + var rect = this.interactionDOMElement.getBoundingClientRect(); + + var changedTouches = event.changedTouches; + for (var i=0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + + var touchData = this.pool.pop(); + if(!touchData)touchData = new PIXI.InteractionData(); + + touchData.originalEvent = event || window.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); + + var length = this.interactiveItems.length; + + for (var j = 0; j < length; j++) + { + var item = this.interactiveItems[j]; + + if(item.touchstart || item.tap) + { + item.__hit = this.hitTest(item, touchData); + + if(item.__hit) + { + //call the function! + if(item.touchstart)item.touchstart(touchData); + item.__isDown = true; + item.__touchData = touchData; + + if(!item.interactiveChildren)break; + } + } + } + } +} + +/** + * Is called when a touch is ended on the renderer element + * + * @method onTouchEnd + * @param event {Event} The DOM event of a touch ending on the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchEnd = function(event) +{ + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); + var changedTouches = event.changedTouches; + + for (var i=0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + var touchData = this.touchs[touchEvent.identifier]; + 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); + + var length = this.interactiveItems.length; + for (var j = 0; j < length; j++) + { + var item = this.interactiveItems[j]; + var itemTouchData = item.__touchData; // <-- Here! + item.__hit = this.hitTest(item, touchData); + + if(itemTouchData == touchData) + { + // so this one WAS down... + touchData.originalEvent = event || window.event; + // hitTest?? + + if(item.touchend || item.tap) + { + if(item.__hit && !up) + { + if(item.touchend)item.touchend(touchData); + if(item.__isDown) + { + if(item.tap)item.tap(touchData); + } + + if(!item.interactiveChildren)up = true; + } + else + { + if(item.__isDown) + { + if(item.touchendoutside)item.touchendoutside(touchData); + } + } + + item.__isDown = false; + } + + item.__touchData = null; + + } + else + { + + } + } + // remove the touch.. + 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; + + /** + * When passed to an event handler, this will be the original DOM Event that was captured + * + * @property originalEvent + * @type Event + */ + this.originalEvent; +} + +/** + * 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; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Stage represents the root of the display tree. Everything connected to the stage is rendered + * + * @class Stage + * @extends DisplayObjectContainer + * @constructor + * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format + * like: 0xFFFFFF for white + */ +PIXI.Stage = function(backgroundColor) +{ + PIXI.DisplayObjectContainer.call( this ); + + /** + * [read-only] Current transform of the object based on world (parent) factors + * + * @property worldTransform + * @type Mat3 + * @readOnly + * @private + */ + this.worldTransform = PIXI.mat3.create(); + + /** + * Whether or not the stage is interactive + * + * @property interactive + * @type Boolean + */ + this.interactive = true; + + /** + * The interaction manage for this stage, manages all interactive activity on the stage + * + * @property interactive + * @type InteractionManager + */ + this.interactionManager = new PIXI.InteractionManager(this); + + /** + * Whether the stage is dirty and needs to have interactions updated + * + * @property dirty + * @type Boolean + * @private + */ + this.dirty = true; + + this.__childrenAdded = []; + this.__childrenRemoved = []; + + //the stage is it's own stage + this.stage = this; + + //optimize hit detection a bit + this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); + + this.setBackgroundColor(backgroundColor); + this.worldVisible = true; +} + +// constructor +PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Stage.prototype.constructor = PIXI.Stage; + +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; + + for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; +} + +/** + * A polyfill for Function.prototype.bind + * + * @method bind + */ +if (typeof Function.prototype.bind != 'function') { + Function.prototype.bind = (function () { + var slice = Array.prototype.slice; + return function (thisArg) { + var target = this, boundArgs = slice.call(arguments, 1); + + if (typeof target != 'function') throw new TypeError(); + + function bound() { + var args = boundArgs.concat(slice.call(arguments)); + target.apply(this instanceof bound ? this : thisArg, args); + } + + bound.prototype = (function F(proto) { + proto && (F.prototype = proto); + if (!(this instanceof F)) return new F; + })(target.prototype); + + return bound; + }; + })(); +} + +/** + * A wrapper for ajax requests to be handled cross browser + * + * @class AjaxRequest + * @constructor + */ +var AjaxRequest = PIXI.AjaxRequest = function() +{ + var activexmodes = ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.3.0", "Microsoft.XMLHTTP"] //activeX versions to check for in IE + + if (window.ActiveXObject) + { //Test for support for ActiveXObject in IE first (as XMLHttpRequest in IE7 is broken) + for (var i=0; i>>>>>>>>") + console.log("_") + var safe = 0; + var tmp = item.first; + console.log(tmp); + + while(tmp._iNext) + { + safe++; + tmp = tmp._iNext; + console.log(tmp); + // console.log(tmp); + + if(safe > 100) + { + console.log("BREAK") + break + } + } +} + + + + + + +/** + * https://github.com/mrdoob/eventtarget.js/ + * THankS mr DOob! + */ + +/** + * Adds event emitter functionality to a class + * + * @class EventTarget + * @example + * function MyEmitter() { + * PIXI.EventTarget.call(this); //mixes in event target stuff + * } + * + * var em = new MyEmitter(); + * em.emit({ type: 'eventName', data: 'some data' }); + */ +PIXI.EventTarget = function () { + + var listeners = {}; + + this.addEventListener = this.on = function ( type, listener ) { + + + if ( listeners[ type ] === undefined ) { + + listeners[ type ] = []; + + } + + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + + listeners[ type ].push( listener ); + } + + }; + + this.dispatchEvent = this.emit = function ( event ) { + + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + + } + + }; + + this.removeEventListener = this.off = function ( type, listener ) { + + var index = listeners[ type ].indexOf( listener ); + + if ( index !== - 1 ) { + + listeners[ type ].splice( index, 1 ); + + } + + }; + +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot fastest. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @method autoDetectRenderer + * @static + * @param width {Number} the width of the renderers view + * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias + */ +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) +{ + if(!width)width = 800; + if(!height)height = 600; + + // BORROWED from Mr Doob (mrdoob.com) + var webgl = ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(); + + //console.log(webgl); + if( webgl ) + { + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); + } + + return new PIXI.CanvasRenderer(width, height, view, transparent); +}; + + + +/* + PolyK library + url: http://polyk.ivank.net + Released under MIT licence. + + Copyright (c) 2012 Ivan Kuckir + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + This is an amazing lib! + + slightly modified by mat groves (matgroves.com); +*/ + +PIXI.PolyK = {}; + +/** + * Triangulates shapes for webGL graphic fills + * + * @method Triangulate + * @namespace PolyK + * @constructor + */ +PIXI.PolyK.Triangulate = function(p) +{ + var sign = true; + + var n = p.length>>1; + if(n<3) return []; + var tgs = []; + var avl = []; + for(var i=0; i 3) + { + var i0 = avl[(i+0)%al]; + var i1 = avl[(i+1)%al]; + var i2 = avl[(i+2)%al]; + + var ax = p[2*i0], ay = p[2*i0+1]; + var bx = p[2*i1], by = p[2*i1+1]; + var cx = p[2*i2], cy = p[2*i2+1]; + + var earFound = false; + if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) + { + earFound = true; + for(var j=0; j 3*al) + { + // need to flip flip reverse it! + // reset! + if(sign) + { + var tgs = []; + avl = []; + for(var i=0; i= 0) && (v >= 0) && (u + v < 1); +} + +/** + * Checks if a shape is convex + * + * @class _convex + * @namespace PolyK + * @private + */ +PIXI.PolyK._convex = function(ax, ay, bx, by, cx, cy, sign) +{ + return ((ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0) == sign; +} + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/* + * the default suoer fast shader! + */ + +PIXI.shaderFragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));", + "gl_FragColor = gl_FragColor * vColor;", + "}" +]; + +PIXI.shaderVertexSrc = [ + "attribute vec2 aVertexPosition;", + "attribute vec2 aTextureCoord;", + "attribute float aColor;", + + "uniform vec2 projectionVector;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "void main(void) {", + "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", + "vTextureCoord = aTextureCoord;", + "vColor = aColor;", + "}" +]; + +/* + * the triangle strip shader.. + */ + +PIXI.stripShaderFragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float alpha;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));", + "gl_FragColor = gl_FragColor * alpha;", + "}" +]; + + +PIXI.stripShaderVertexSrc = [ + "attribute vec2 aVertexPosition;", + "attribute vec2 aTextureCoord;", + "attribute float aColor;", + "uniform mat3 translationMatrix;", + "uniform vec2 projectionVector;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "void main(void) {", + "vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);", + "gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);", + "vTextureCoord = aTextureCoord;", + "vColor = aColor;", + "}" +]; + +/* + * primitive shader.. + */ + +PIXI.primitiveShaderFragmentSrc = [ + "precision mediump float;", + "varying vec4 vColor;", + "void main(void) {", + "gl_FragColor = vColor;", + "}" +]; + +PIXI.primitiveShaderVertexSrc = [ + "attribute vec2 aVertexPosition;", + "attribute vec4 aColor;", + "uniform mat3 translationMatrix;", + "uniform vec2 projectionVector;", + "uniform float alpha;", + "varying vec4 vColor;", + "void main(void) {", + "vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);", + "gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);", + "vColor = aColor * alpha;", + "}" +]; + +PIXI.shaderStack = []; + +PIXI.initPrimitiveShader = function() +{ + var gl = PIXI.gl; + + var shaderProgram = PIXI.compileProgram(PIXI.primitiveShaderVertexSrc, PIXI.primitiveShaderFragmentSrc) + + gl.useProgram(shaderProgram); + + shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); + shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); + + shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); + shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); + + PIXI.primitiveProgram = shaderProgram; + + +} + +PIXI.initDefaultShader = function() +{ + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ +} + +PIXI.initDefaultStripShader = function() +{ + var gl = this.gl; + var shaderProgram = PIXI.compileProgram(PIXI.stripShaderVertexSrc, PIXI.stripShaderFragmentSrc) + + gl.useProgram(shaderProgram); + + shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); + shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); + shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); + shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); + + shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); + shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); + shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); + + PIXI.stripShaderProgram = shaderProgram; +} + +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)) { + alert(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)) { + alert("Could not initialise shaders"); + } + + return shaderProgram; +} + +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() +{ + var gl = PIXI.gl; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; + + gl.useProgram(shaderProgram); + + PIXI.currentShader = shaderProgram; +} + +PIXI.activatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.primitiveProgram); + + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); +} + +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A set of functions used by the webGL renderer to draw the primitive graphics data + * + * @class CanvasGraphics + */ +PIXI.WebGLGraphics = function() +{ + +} + +/** + * Renders the graphics object + * + * @static + * @private + * @method renderGraphics + * @param graphics {Graphics} + * @param projection {Object} + */ +PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) +{ + var gl = PIXI.gl; + + if(!graphics._webGL)graphics._webGL = {points:[], indices:[], lastIndex:0, + buffer:gl.createBuffer(), + indexBuffer:gl.createBuffer()}; + + if(graphics.dirty) + { + graphics.dirty = false; + + if(graphics.clearDirty) + { + graphics.clearDirty = false; + + graphics._webGL.lastIndex = 0; + graphics._webGL.points = []; + graphics._webGL.indices = []; + + } + + PIXI.WebGLGraphics.updateGraphics(graphics); + } + + + PIXI.activatePrimitiveShader(); + + // This could be speeded up fo sure! + var m = PIXI.mat3.clone(graphics.worldTransform); + + PIXI.mat3.transpose(m); + + // set the matrix transform for the + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + + gl.uniformMatrix3fv(PIXI.primitiveProgram.translationMatrix, false, m); + + gl.uniform2f(PIXI.primitiveProgram.projectionVector, projection.x, projection.y); + + gl.uniform1f(PIXI.primitiveProgram.alpha, graphics.worldAlpha); + + gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); + + // WHY DOES THIS LINE NEED TO BE THERE??? + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + // its not even used.. but need to be set or it breaks? + // only on pc though.. + + gl.vertexAttribPointer(PIXI.primitiveProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 4 * 6, 0); + gl.vertexAttribPointer(PIXI.primitiveProgram.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); + + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.indexBuffer); + + gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + + PIXI.deactivatePrimitiveShader(); + + // return to default shader... +// PIXI.activateShader(PIXI.defaultShader); +} + +/** + * Updates the graphics object + * + * @static + * @private + * @method updateGraphics + * @param graphics {Graphics} + */ +PIXI.WebGLGraphics.updateGraphics = function(graphics) +{ + for (var i=graphics._webGL.lastIndex; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + if(data.type == PIXI.Graphics.POLY) + { + if(data.fill) + { + if(data.points.length>3) + PIXI.WebGLGraphics.buildPoly(data, graphics._webGL); + } + + if(data.lineWidth > 0) + { + PIXI.WebGLGraphics.buildLine(data, graphics._webGL); + } + } + else if(data.type == PIXI.Graphics.RECT) + { + PIXI.WebGLGraphics.buildRectangle(data, graphics._webGL); + } + else if(data.type == PIXI.Graphics.CIRC || data.type == PIXI.Graphics.ELIP) + { + PIXI.WebGLGraphics.buildCircle(data, graphics._webGL); + } + }; + + graphics._webGL.lastIndex = graphics.graphicsData.length; + + var gl = PIXI.gl; + + graphics._webGL.glPoints = new Float32Array(graphics._webGL.points); + + gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); + gl.bufferData(gl.ARRAY_BUFFER, graphics._webGL.glPoints, gl.STATIC_DRAW); + + graphics._webGL.glIndicies = new Uint16Array(graphics._webGL.indices); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.glIndicies, gl.STATIC_DRAW); +} + +/** + * Builds a rectangle to draw + * + * @static + * @private + * @method buildRectangle + * @param graphics {Graphics} + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) +{ + // --- // + // need to convert points to a nice regular data + // + var rectData = graphicsData.points; + var x = rectData[0]; + var y = rectData[1]; + var width = rectData[2]; + var height = rectData[3]; + + + if(graphicsData.fill) + { + var color = HEXtoRGB(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vertPos = verts.length/6; + + // start + verts.push(x, y); + verts.push(r, g, b, alpha); + + verts.push(x + width, y); + verts.push(r, g, b, alpha); + + verts.push(x , y + height); + verts.push(r, g, b, alpha); + + verts.push(x + width, y + height); + verts.push(r, g, b, alpha); + + // insert 2 dead triangles.. + indices.push(vertPos, vertPos, vertPos+1, vertPos+2, vertPos+3, vertPos+3) + } + + if(graphicsData.lineWidth) + { + graphicsData.points = [x, y, + x + width, y, + x + width, y + height, + x, y + height, + x, y]; + + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + } + +} + +/** + * Builds a circle to draw + * + * @static + * @private + * @method buildCircle + * @param graphics {Graphics} + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) +{ + // --- // + // need to convert points to a nice regular data + // + var rectData = graphicsData.points; + var x = rectData[0]; + var y = rectData[1]; + var width = rectData[2]; + var height = rectData[3]; + + var totalSegs = 40; + var seg = (Math.PI * 2) / totalSegs ; + + if(graphicsData.fill) + { + var color = HEXtoRGB(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vecPos = verts.length/6; + + indices.push(vecPos); + + for (var i=0; i < totalSegs + 1 ; i++) + { + verts.push(x,y, r, g, b, alpha); + + verts.push(x + Math.sin(seg * i) * width, + y + Math.cos(seg * i) * height, + r, g, b, alpha); + + indices.push(vecPos++, vecPos++); + }; + + indices.push(vecPos-1); + } + + if(graphicsData.lineWidth) + { + graphicsData.points = []; + + for (var i=0; i < totalSegs + 1; i++) + { + graphicsData.points.push(x + Math.sin(seg * i) * width, + y + Math.cos(seg * i) * height) + }; + + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + } + +} + +/** + * Builds a line to draw + * + * @static + * @private + * @method buildLine + * @param graphics {Graphics} + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) +{ + // TODO OPTIMISE! + + var wrap = true; + var points = graphicsData.points; + if(points.length == 0)return; + + // get first and last point.. figure out the middle! + 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(firstPoint.x == lastPoint.x && firstPoint.y == lastPoint.y) + { + points.pop(); + points.pop(); + + lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); + + var midPointX = lastPoint.x + (firstPoint.x - lastPoint.x) *0.5; + var midPointY = lastPoint.y + (firstPoint.y - lastPoint.y) *0.5; + + points.unshift(midPointX, midPointY); + points.push(midPointX, midPointY) + } + + var verts = webGLData.points; + var indices = webGLData.indices; + var length = points.length / 2; + var indexCount = points.length; + var indexStart = verts.length/6; + + // DRAW the Line + var width = graphicsData.lineWidth / 2; + + // sort color + var color = HEXtoRGB(graphicsData.lineColor); + var alpha = graphicsData.lineAlpha; + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var p1x, p1y, p2x, p2y, p3x, p3y; + var perpx, perpy, perp2x, perp2y, perp3x, perp3y; + var ipx, ipy; + var a1, b1, c1, a2, b2, c2; + var denom, pdist, dist; + + p1x = points[0]; + p1y = points[1]; + + p2x = points[2]; + p2y = points[3]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + // start + verts.push(p1x - perpx , p1y - perpy, + r, g, b, alpha); + + verts.push(p1x + perpx , p1y + perpy, + r, g, b, alpha); + + for (var i = 1; i < length-1; i++) + { + p1x = points[(i-1)*2]; + p1y = points[(i-1)*2 + 1]; + + p2x = points[(i)*2] + p2y = points[(i)*2 + 1] + + p3x = points[(i+1)*2]; + p3y = points[(i+1)*2 + 1]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + perp2x = -(p2y - p3y); + perp2y = p2x - p3x; + + dist = Math.sqrt(perp2x*perp2x + perp2y*perp2y); + perp2x /= dist; + perp2y /= dist; + perp2x *= width; + perp2y *= width; + + a1 = (-perpy + p1y) - (-perpy + p2y); + b1 = (-perpx + p2x) - (-perpx + p1x); + c1 = (-perpx + p1x) * (-perpy + p2y) - (-perpx + p2x) * (-perpy + p1y); + a2 = (-perp2y + p3y) - (-perp2y + p2y); + b2 = (-perp2x + p2x) - (-perp2x + p3x); + c2 = (-perp2x + p3x) * (-perp2y + p2y) - (-perp2x + p2x) * (-perp2y + p3y); + + denom = a1*b2 - a2*b1; + + if (denom == 0) { + denom+=1; + } + + px = (b1*c2 - b2*c1)/denom; + py = (a2*c1 - a1*c2)/denom; + + pdist = (px -p2x) * (px -p2x) + (py -p2y) + (py -p2y); + + if(pdist > 140 * 140) + { + perp3x = perpx - perp2x; + perp3y = perpy - perp2y; + + dist = Math.sqrt(perp3x*perp3x + perp3y*perp3y); + perp3x /= dist; + perp3y /= dist; + perp3x *= width; + perp3y *= width; + + verts.push(p2x - perp3x, p2y -perp3y); + verts.push(r, g, b, alpha); + + verts.push(p2x + perp3x, p2y +perp3y); + verts.push(r, g, b, alpha); + + verts.push(p2x - perp3x, p2y -perp3y); + verts.push(r, g, b, alpha); + + indexCount++; + } + else + { + verts.push(px , py); + verts.push(r, g, b, alpha); + + verts.push(p2x - (px-p2x), p2y - (py - p2y)); + verts.push(r, g, b, alpha); + } + } + + p1x = points[(length-2)*2] + p1y = points[(length-2)*2 + 1] + + p2x = points[(length-1)*2] + p2y = points[(length-1)*2 + 1] + + perpx = -(p1y - p2y) + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + verts.push(p2x - perpx , p2y - perpy) + verts.push(r, g, b, alpha); + + verts.push(p2x + perpx , p2y + perpy) + verts.push(r, g, b, alpha); + + indices.push(indexStart); + + for (var i=0; i < indexCount; i++) + { + indices.push(indexStart++); + }; + + indices.push(indexStart-1); +} + +/** + * Builds a polygon to draw + * + * @static + * @private + * @method buildPoly + * @param graphics {Graphics} + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) +{ + var points = graphicsData.points; + if(points.length < 6)return; + + // get first and last point.. figure out the middle! + var verts = webGLData.points; + var indices = webGLData.indices; + + var length = points.length / 2; + + // sort color + var color = HEXtoRGB(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var triangles = PIXI.PolyK.Triangulate(points); + + var vertPos = verts.length / 6; + + for (var i=0; i < triangles.length; i+=3) + { + indices.push(triangles[i] + vertPos); + indices.push(triangles[i] + vertPos); + indices.push(triangles[i+1] + vertPos); + indices.push(triangles[i+2] +vertPos); + indices.push(triangles[i+2] + vertPos); + }; + + for (var i = 0; i < length; i++) + { + verts.push(points[i * 2], points[i * 2 + 1], + r, g, b, alpha); + }; +} + +function HEXtoRGB(hex) { + return [(hex >> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; +} + + + + + +/** + * @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; + +/** + * 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. + * 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 :) + * + * @class WebGLRenderer + * @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 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.. + + this.transparent = !!transparent; + + this.width = width || 800; + this.height = height || 600; + + 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) + + this.batchs = []; + + try + { + PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { + alpha: this.transparent, + antialias:!!antialias, // SPEED UP?? + premultipliedAlpha:false, + stencil:true + }); + } + catch (e) + { + throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); + } + + PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); + PIXI.initDefaultStripShader(); + + +// PIXI.activateDefaultShader(); + + var gl = this.gl; + PIXI.WebGLRenderer.gl = gl; + + this.batch = new PIXI.WebGLBatch(gl); + 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); + + this.resize(this.width, this.height); + this.contextLost = false; + + PIXI.activateShader(PIXI.defaultShader); + + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + +} + +// 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 + * + * @method render + * @param stage {Stage} the Stage element to be rendered + */ +PIXI.WebGLRenderer.prototype.render = function(stage) +{ + if(this.contextLost)return; + + + // if rendering a new stage clear the batchs.. + if(this.__stage !== stage) + { + // TODO make this work + // dont think this is needed any more? + this.__stage = stage; + this.stageRenderGroup.setRenderable(stage); + } + + // TODO not needed now... + // update children if need be + // best to remove first! + /*for (var i=0; i < stage.__childrenRemoved.length; i++) + { + var group = stage.__childrenRemoved[i].__renderGroup + if(group)group.removeDisplayObject(stage.__childrenRemoved[i]); + }*/ + + // update any textures + 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.viewport(0, 0, this.width, this.height); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); + gl.clear(gl.COLOR_BUFFER_BIT); + + // HACK TO TEST + + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; + this.stageRenderGroup.render(PIXI.projection); + + // interaction + // run interaction! + if(stage.interactive) + { + //need to add some events! + if(!stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = true; + stage.interactionManager.setTarget(this); + } + } + + // after rendering lets confirm all frames that have been uodated.. + if(PIXI.Texture.frameUpdates.length > 0) + { + for (var i=0; i < PIXI.Texture.frameUpdates.length; i++) + { + PIXI.Texture.frameUpdates[i].updateFrame = false; + }; + + PIXI.Texture.frameUpdates = []; + } +} + +/** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures + * @private + */ +PIXI.WebGLRenderer.updateTextures = function() +{ + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var 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, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + // 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); + } +} + +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) +{ + //TODO break this out into a texture manager... + var gl = PIXI.gl; + + if(texture._glTexture) + { + texture._glTexture = gl.createTexture(); + gl.deleteTexture(gl.TEXTURE_2D, texture._glTexture); + } +} + +/** + * resizes the webGL view to the specified width and height + * + * @method resize + * @param width {Number} the new width of the webGL view + * @param height {Number} the new height of the webGL view + */ +PIXI.WebGLRenderer.prototype.resize = function(width, height) +{ + this.width = width; + this.height = height; + + this.view.width = width; + this.view.height = height; + + this.gl.viewport(0, 0, this.width, this.height); + + //var projectionMatrix = this.projectionMatrix; + + PIXI.projection.x = this.width/2; + PIXI.projection.y = this.height/2; + +// projectionMatrix[0] = 2/this.width; +// projectionMatrix[5] = -2/this.height; +// projectionMatrix[12] = -1; +// projectionMatrix[13] = 1; +} + +/** + * Handles a lost webgl context + * + * @method handleContextLost + * @param event {Event} + * @private + */ +PIXI.WebGLRenderer.prototype.handleContextLost = function(event) +{ + event.preventDefault(); + this.contextLost = true; +} + +/** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} + * @private + */ +PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) +{ + this.gl = this.view.getContext("experimental-webgl", { + alpha: true + }); + + this.initShaders(); + + for(var key in PIXI.TextureCache) + { + var texture = PIXI.TextureCache[key].baseTexture; + texture._glTexture = null; + PIXI.WebGLRenderer.updateTexture(texture); + }; + + for (var i=0; i < this.batchs.length; i++) + { + this.batchs[i].restoreLostContext(this.gl)// + this.batchs[i].dirty = true; + }; + + PIXI._restoreBatchs(this.gl); + + this.contextLost = false; +} + +/** + * @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; + this.tail; +} + +/** + * 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() +{ + var gl = this.gl; + + if (this.dynamicSize < this.size) + { + this.growBatch(); + } + + var indexRun = 0; + var worldTransform, width, height, aX, aY, w0, w1, h0, h1, index; + var a, b, c, d, tx, ty; + + 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 gl = this.gl; + var worldTransform, width, height, aX, aY, w0, w1, h0, h1, index, index2, index3 + + var a, b, c, d, tx, ty; + + var indexRun = 0; + + var displayObject = this.head; + + 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]; + + this.verticies[index + 0 ] = a * w1 + c * h1 + tx; + this.verticies[index + 1 ] = d * h1 + b * w1 + ty; + + this.verticies[index + 2 ] = a * w0 + c * h1 + tx; + this.verticies[index + 3 ] = d * h1 + b * w0 + ty; + + this.verticies[index + 4 ] = a * w0 + c * h0 + tx; + this.verticies[index + 5 ] = d * h0 + b * w0 + ty; + + this.verticies[index + 6] = a * w1 + c * h0 + tx; + this.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; + + 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; + } + + // TODO this probably could do with some optimisation.... + if(displayObject.cacheAlpha != displayObject.worldAlpha) + { + displayObject.cacheAlpha = displayObject.worldAlpha; + + var colorIndex = indexRun * 4; + this.colors[colorIndex] = this.colors[colorIndex + 1] = this.colors[colorIndex + 2] = this.colors[colorIndex + 3] = displayObject.worldAlpha; + this.dirtyColors = true; + } + } + else + { + index = indexRun * 8; + + this.verticies[index + 0 ] = 0; + this.verticies[index + 1 ] = 0; + + this.verticies[index + 2 ] = 0; + this.verticies[index + 3 ] = 0; + + this.verticies[index + 4 ] = 0; + this.verticies[index + 5 ] = 0; + + this.verticies[index + 6] = 0; + this.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.currentShader; + + //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.vertexPositionAttribute, 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.textureCoordAttribute, 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 ); +} + +/** + * @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) +{ + this.gl = gl; + this.root; + + this.backgroundColor; + this.batchs = []; + this.toRemove = []; +} + +// 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) +{ + PIXI.WebGLRenderer.updateTextures(); + + var gl = this.gl; + + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + + 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; + } + + // non sprite batch.. + 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.Graphics) + { + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + } + else if(renderable instanceof PIXI.FilterBlock) + { + this.handleFilterBlock(renderable, projection); + } + } + +} + +/** + * Renders a specific displayObject + * + * @method renderSpecific + * @param displayObject {DisplayObject} + * @param projection {Object} + * @private + */ +PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projection) +{ + PIXI.WebGLRenderer.updateTextures(); + + var gl = this.gl; + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + + // 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) + { + nextRenderable = nextRenderable._iNext; + if(nextRenderable.renderable && nextRenderable.__renderGroup)break; + } + var startBatch = nextRenderable.batch; + + 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; + var endBatch; + var lastItem = displayObject; + while(lastItem.children.length > 0) + { + lastItem = lastItem.children[lastItem.children.length-1]; + if(lastItem.renderable)lastRenderable = lastItem.last; + } + + 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; + } + + // TODO - need to fold this up a bit! + + 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 sta = PIXI.shaderStack.length; + + 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); + } +} + +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; + + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else + { + gl.enable(gl.STENCIL_TEST); + + gl.colorMask(false, false, false, false); + gl.stencilFunc(gl.ALWAYS,1,0xff); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL,0,0xff); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); + } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else + { + 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; + var shaderProgram = PIXI.stripShaderProgram; + + + gl.useProgram(shaderProgram); + + var m = PIXI.mat3.clone(strip.worldTransform); + + PIXI.mat3.transpose(m); + + // set the matrix transform for the + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.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); + } + */ + + + if(!strip.dirty) + { + + gl.bindBuffer(gl.ARRAY_BUFFER, strip._vertexBuffer); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, strip.verticies) + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + + // update the uvs + gl.bindBuffer(gl.ARRAY_BUFFER, strip._uvBuffer); + gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, 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(shaderProgram.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(shaderProgram.vertexPositionAttribute, 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(shaderProgram.textureCoordAttribute, 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.bufferData(gl.ARRAY_BUFFER, strip.colors, gl.STATIC_DRAW) + gl.vertexAttribPointer(shaderProgram.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); + + 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); +} + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * the CanvasRenderer draws the stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * Dont forget to add the view to your DOM or you will not see anything :) + * + * @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 + */ +PIXI.CanvasRenderer = function(width, height, view, transparent) +{ + 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 the everything is drawn to + * + * @property view + * @type Canvas + */ + this.view = view || document.createElement( 'canvas' ); + + /** + * The canvas context that the everything is drawn to + * @property context + * @type Canvas 2d Context + */ + this.context = this.view.getContext("2d"); + + this.refresh = true; + // hack to enable some hardware acceleration! + //this.view.style["transform"] = "translatez(0)"; + + this.view.width = this.width; + this.view.height = this.height; + this.count = 0; +} + +// constructor +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; + +/** + * Renders the stage to its canvas view + * + * @method render + * @param stage {Stage} the Stage element to be rendered + */ +PIXI.CanvasRenderer.prototype.render = function(stage) +{ + + //stage.__childrenAdded = []; + //stage.__childrenRemoved = []; + + // update textures if need be + PIXI.texturesToUpdate = []; + PIXI.texturesToDestroy = []; + + PIXI.visibleCount++; + stage.updateTransform(); + + // update the background color + 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) + this.renderDisplayObject(stage); + //as + + // run interaction! + if(stage.interactive) + { + //need to add some events! + if(!stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = true; + stage.interactionManager.setTarget(this); + } + } + + // remove frame updates.. + if(PIXI.Texture.frameUpdates.length > 0) + { + PIXI.Texture.frameUpdates = []; + } + + +} + +/** + * resizes the canvas view to the specified width and height + * + * @method resize + * @param width {Number} the new width of the canvas view + * @param height {Number} the new height of the canvas view + */ +PIXI.CanvasRenderer.prototype.resize = function(width, height) +{ + this.width = width; + this.height = height; + + this.view.width = width; + this.view.height = height; +} + +/** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render + * @private + */ +PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) +{ + // no loger recurrsive! + 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; + + if(frame && frame.width && frame.height) + { + context.globalAlpha = displayObject.worldAlpha; + + context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); + + 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) + { + 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(PIXI.FilterBlock.data instanceof PIXI.Graphics) + { + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } + } + else + { + // only masks supported right now! + } + } + // count++ + displayObject = displayObject._iNext; + + + } + while(displayObject != testObject) + + +} + +/** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render + * @private + */ +PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) +{ + var context = this.context; + var verticies = strip.verticies; + var uvs = strip.uvs; + + var length = verticies.length/2; + this.count++; + + context.beginPath(); + for (var i=1; i < length-2; i++) + { + + // draw some triangles! + var index = i*2; + + var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4]; + var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + + }; + + 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 + * + * @method renderStrip + * @param strip {Strip} The Strip to render + * @private + */ +PIXI.CanvasRenderer.prototype.renderStrip = function(strip) +{ + var context = this.context; + + // draw triangles!! + var verticies = strip.verticies; + var uvs = strip.uvs; + + var length = verticies.length/2; + this.count++; + for (var i=1; i < length-2; i++) + { + + // draw some triangles! + var index = i*2; + + var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4]; + var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5]; + + var u0 = uvs[index] * strip.texture.width, u1 = uvs[index+2] * strip.texture.width, u2 = uvs[index+4]* strip.texture.width; + var v0 = uvs[index+1]* strip.texture.height, v1 = uvs[index+3] * strip.texture.height, v2 = uvs[index+5]* strip.texture.height; + + + context.save(); + context.beginPath(); + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + context.closePath(); + + context.clip(); + + + // Compute matrix transform + var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2; + var delta_a = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2; + var delta_b = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2; + var delta_c = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2 - v0*u1*x2 - u0*x1*v2; + var delta_d = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2; + var delta_e = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2; + var delta_f = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2 - v0*u1*y2 - u0*y1*v2; + + + + + context.transform(delta_a/delta, delta_d/delta, + delta_b/delta, delta_e/delta, + delta_c/delta, delta_f/delta); + + context.drawImage(strip.texture.baseTexture.source, 0, 0); + context.restore(); + }; + +} + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * A set of functions used by the canvas renderer to draw the primitive graphics data + * + * @class CanvasGraphics + */ +PIXI.CanvasGraphics = function() +{ + +} + + +/* + * Renders the graphics object + * + * @static + * @private + * @method renderGraphics + * @param graphics {Graphics} + * @param context {Context2D} + */ +PIXI.CanvasGraphics.renderGraphics = function(graphics, context) +{ + var worldAlpha = graphics.worldAlpha; + + for (var i=0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + var points = data.points; + + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); + + context.lineWidth = data.lineWidth; + + if(data.type == PIXI.Graphics.POLY) + { + context.beginPath(); + + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + // if the first and last point are the same close the path - much neater :) + if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) + { + context.closePath(); + } + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.stroke(); + } + } + else if(data.type == PIXI.Graphics.RECT) + { + + if(data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fillRect(points[0], points[1], points[2], points[3]); + + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeRect(points[0], points[1], points[2], points[3]); + } + + } + else if(data.type == PIXI.Graphics.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(points[0], points[1], points[2],0,2*Math.PI); + context.closePath(); + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.stroke(); + } + } + else if(data.type == PIXI.Graphics.ELIP) + { + + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + + var elipseData = data.points; + + var w = elipseData[2] * 2; + var h = elipseData[3] * 2; + + var x = elipseData[0] - w/2; + var y = elipseData[1] - h/2; + + context.beginPath(); + + var kappa = .5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + context.closePath(); + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.stroke(); + } + } + + }; +} + +/* + * Renders a graphics mask + * + * @static + * @private + * @method renderGraphicsMask + * @param graphics {Graphics} + * @param context {Context2D} + */ +PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) +{ + var worldAlpha = graphics.worldAlpha; + + var len = graphics.graphicsData.length; + if(len > 1) + { + len = 1; + console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") + } + + for (var i=0; i < 1; i++) + { + var data = graphics.graphicsData[i]; + var points = data.points; + + if(data.type == PIXI.Graphics.POLY) + { + context.beginPath(); + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + // if the first and last point are the same close the path - much neater :) + if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) + { + context.closePath(); + } + + } + else if(data.type == PIXI.Graphics.RECT) + { + context.beginPath(); + context.rect(points[0], points[1], points[2], points[3]); + context.closePath(); + } + else if(data.type == PIXI.Graphics.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(points[0], points[1], points[2],0,2*Math.PI); + context.closePath(); + } + else if(data.type == PIXI.Graphics.ELIP) + { + + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + var elipseData = data.points; + + var w = elipseData[2] * 2; + var h = elipseData[3] * 2; + + var x = elipseData[0] - w/2; + var y = elipseData[1] - h/2; + + context.beginPath(); + + var kappa = .5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + context.closePath(); + } + + + }; +} + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * 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 + * + * @class Graphics + * @extends DisplayObjectContainer + * @constructor + */ +PIXI.Graphics = function() +{ + PIXI.DisplayObjectContainer.call( this ); + + this.renderable = true; + + /** + * The alpha of the fill of this graphics object + * + * @property fillAlpha + * @type Number + */ + this.fillAlpha = 1; + + /** + * The width of any lines drawn + * + * @property lineWidth + * @type Number + */ + this.lineWidth = 0; + + /** + * The color of any lines drawn + * + * @property lineColor + * @type String + */ + this.lineColor = "black"; + + /** + * Graphics data + * + * @property graphicsData + * @type Array + * @private + */ + this.graphicsData = []; + + /** + * Current path + * + * @property currentPath + * @type Object + * @private + */ + this.currentPath = {points:[]}; +} + +// constructor +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. + * + * @method lineStyle + * @param lineWidth {Number} width of the line to draw, will update the object's stored style + * @param color {Number} color of the line to draw, will update the object's stored style + * @param alpha {Number} alpha of the line to draw, will update the object's stored style + */ +PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) +{ + if(this.currentPath.points.length == 0)this.graphicsData.pop(); + + this.lineWidth = lineWidth || 0; + this.lineColor = color || 0; + this.lineAlpha = (alpha == undefined) ? 1 : alpha; + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; + + this.graphicsData.push(this.currentPath); +} + +/** + * 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 + */ +PIXI.Graphics.prototype.moveTo = function(x, y) +{ + if(this.currentPath.points.length == 0)this.graphicsData.pop(); + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; + + this.currentPath.points.push(x, y); + + this.graphicsData.push(this.currentPath); +} + +/** + * Draws a line using the current line style from the current drawing position to (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 + */ +PIXI.Graphics.prototype.lineTo = function(x, y) +{ + this.currentPath.points.push(x, y); + this.dirty = true; +} + +/** + * Specifies a simple one-color fill that subsequent calls to other Graphics methods + * (such as lineTo() or drawCircle()) use when drawing. + * + * @method beginFill + * @param color {uint} the color of the fill + * @param alpha {Number} the alpha + */ +PIXI.Graphics.prototype.beginFill = function(color, alpha) +{ + this.filling = true; + this.fillColor = color || 0; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; +} + +/** + * Applies a fill to the lines and shapes that were added since the last call to the beginFill() method. + * + * @method endFill + */ +PIXI.Graphics.prototype.endFill = function() +{ + this.filling = false; + this.fillColor = null; + this.fillAlpha = 1; +} + +/** + * @method drawRect + * + * @param x {Number} The X coord of the top-left of the rectangle + * @param y {Number} The Y coord of the top-left of the rectangle + * @param width {Number} The width of the rectangle + * @param height {Number} The height of the rectangle + */ +PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) +{ + if(this.currentPath.points.length == 0)this.graphicsData.pop(); + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + points:[x, y, width, height], type:PIXI.Graphics.RECT}; + + this.graphicsData.push(this.currentPath); + this.dirty = true; +} + +/** + * 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 radius {Number} The radius of the circle + */ +PIXI.Graphics.prototype.drawCircle = function( x, y, radius) +{ + if(this.currentPath.points.length == 0)this.graphicsData.pop(); + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; + + this.graphicsData.push(this.currentPath); + this.dirty = true; +} + +/** + * Draws an elipse. + * + * @method drawElipse + * @param x {Number} + * @param y {Number} + * @param width {Number} + * @param height {Number} + */ +PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) +{ + if(this.currentPath.points.length == 0)this.graphicsData.pop(); + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + points:[x, y, width, height], type:PIXI.Graphics.ELIP}; + + this.graphicsData.push(this.currentPath); + this.dirty = true; +} + +/** + * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings. + * + * @method clear + */ +PIXI.Graphics.prototype.clear = function() +{ + this.lineWidth = 0; + this.filling = false; + + this.dirty = true; + this.clearDirty = true; + this.graphicsData = []; +} + +// SOME TYPES: +PIXI.Graphics.POLY = 0; +PIXI.Graphics.RECT = 1; +PIXI.Graphics.CIRC = 2; +PIXI.Graphics.ELIP = 3; + +/** + * @author Mat Groves http://matgroves.com/ + */ + +PIXI.Strip = function(texture, width, height) +{ + PIXI.DisplayObjectContainer.call( this ); + this.texture = texture; + this.blendMode = PIXI.blendModes.NORMAL; + + try + { + this.uvs = new Float32Array([0, 1, + 1, 1, + 1, 0, 0,1]); + + this.verticies = new Float32Array([0, 0, + 0,0, + 0,0, 0, + 0, 0]); + + this.colors = new Float32Array([1, 1, 1, 1]); + + this.indices = new Uint16Array([0, 1, 2, 3]); + } + catch(error) + { + this.uvs = [0, 1, + 1, 1, + 1, 0, 0,1]; + + this.verticies = [0, 0, + 0,0, + 0,0, 0, + 0, 0]; + + this.colors = [1, 1, 1, 1]; + + this.indices = [0, 1, 2, 3]; + } + + + /* + this.uvs = new Float32Array() + this.verticies = new Float32Array() + this.colors = new Float32Array() + this.indices = new Uint16Array() +*/ + this.width = width; + this.height = height; + + // load the texture! + if(texture.baseTexture.hasLoaded) + { + this.width = this.texture.frame.width; + this.height = this.texture.frame.height; + this.updateFrame = true; + } + else + { + this.onTextureUpdateBind = this.onTextureUpdate.bind(this); + this.texture.addEventListener( 'update', this.onTextureUpdateBind ); + } + + this.renderable = true; +} + +// constructor +PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; + +PIXI.Strip.prototype.setTexture = function(texture) +{ + //TODO SET THE TEXTURES + //TODO VISIBILITY + + // stop current texture + this.texture = texture; + this.width = texture.frame.width; + this.height = texture.frame.height; + this.updateFrame = true; +} + +PIXI.Strip.prototype.onTextureUpdate = function(event) +{ + this.updateFrame = true; +} +// some helper functions.. + + +/** + * @author Mat Groves http://matgroves.com/ + */ + + +PIXI.Rope = function(texture, points) +{ + PIXI.Strip.call( this, texture ); + this.points = points; + + try + { + this.verticies = new Float32Array( points.length * 4); + this.uvs = new Float32Array( points.length * 4); + this.colors = new Float32Array( points.length * 2); + this.indices = new Uint16Array( points.length * 2); + } + catch(error) + { + this.verticies = verticies + + this.uvs = uvs + this.colors = colors + this.indices = indices + } + + this.refresh(); +} + + +// constructor +PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; + +PIXI.Rope.prototype.refresh = function() +{ + var points = this.points; + if(points.length < 1)return; + + var uvs = this.uvs + var indices = this.indices; + var colors = this.colors; + + var lastPoint = points[0]; + var nextPoint; + var perp = {x:0, y:0}; + var point = points[0]; + + this.count-=0.2; + + + uvs[0] = 0 + uvs[1] = 1 + uvs[2] = 0 + uvs[3] = 1 + + colors[0] = 1; + colors[1] = 1; + + indices[0] = 0; + indices[1] = 1; + + var total = points.length; + + for (var i = 1; i < total; i++) + { + + var point = points[i]; + var index = i * 4; + // time to do some smart drawing! + var amount = i/(total-1) + + if(i%2) + { + uvs[index] = amount; + uvs[index+1] = 0; + + uvs[index+2] = amount + uvs[index+3] = 1 + + } + else + { + uvs[index] = amount + uvs[index+1] = 0 + + uvs[index+2] = amount + uvs[index+3] = 1 + } + + index = i * 2; + colors[index] = 1; + colors[index+1] = 1; + + index = i * 2; + indices[index] = index; + indices[index + 1] = index + 1; + + lastPoint = point; + } +} + +PIXI.Rope.prototype.updateTransform = function() +{ + + var points = this.points; + if(points.length < 1)return; + + var verticies = this.verticies + + var lastPoint = points[0]; + var nextPoint; + var perp = {x:0, y:0}; + var point = points[0]; + + this.count-=0.2; + + verticies[0] = point.x + perp.x + verticies[1] = point.y + perp.y //+ 200 + verticies[2] = point.x - perp.x + verticies[3] = point.y - perp.y//+200 + // time to do some smart drawing! + + var total = points.length; + + for (var i = 1; i < total; i++) + { + + var point = points[i]; + var index = i * 4; + + if(i < points.length-1) + { + nextPoint = points[i+1]; + } + else + { + nextPoint = point + } + + perp.y = -(nextPoint.x - lastPoint.x); + perp.x = nextPoint.y - lastPoint.y; + + var ratio = (1 - (i / (total-1))) * 10; + if(ratio > 1)ratio = 1; + + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); + var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; + perp.x /= perpLength; + perp.y /= perpLength; + + perp.x *= num; + perp.y *= num; + + verticies[index] = point.x + perp.x + verticies[index+1] = point.y + perp.y + verticies[index+2] = point.x - perp.x + verticies[index+3] = point.y - perp.y + + lastPoint = point; + } + + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); +} + +PIXI.Rope.prototype.setTexture = function(texture) +{ + // stop current texture + this.texture = texture; + this.updateFrame = true; +} + + + + + +/** + * @author Mat Groves http://matgroves.com/ + */ + +/** + * A tiling sprite is a fast way of rendering a tiling image + * + * @class TilingSprite + * @extends DisplayObjectContainer + * @constructor + * @param texture {Texture} the texture of the tiling sprite + * @param width {Number} the width of the tiling sprite + * @param height {Number} the height of the tiling sprite + */ +PIXI.TilingSprite = function(texture, width, height) +{ + PIXI.DisplayObjectContainer.call( this ); + + /** + * The texture that the sprite is using + * + * @property texture + * @type Texture + */ + this.texture = texture; + + /** + * The width of the tiling sprite + * + * @property width + * @type Number + */ + this.width = width; + + /** + * The height of the tiling sprite + * + * @property height + * @type Number + */ + this.height = height; + + /** + * The scaling of the image that is being tiled + * + * @property tileScale + * @type Point + */ + this.tileScale = new PIXI.Point(1,1); + + /** + * The offset position of the image that is being tiled + * + * @property tilePosition + * @type Point + */ + this.tilePosition = new PIXI.Point(0,0); + + this.renderable = true; + + this.blendMode = PIXI.blendModes.NORMAL +} + +// constructor +PIXI.TilingSprite.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.TilingSprite.prototype.constructor = PIXI.TilingSprite; + +/** + * Sets the texture of the tiling sprite + * + * @method setTexture + * @param texture {Texture} The PIXI texture that is displayed by the sprite + */ +PIXI.TilingSprite.prototype.setTexture = function(texture) +{ + //TODO SET THE TEXTURES + //TODO VISIBILITY + + // stop current texture + this.texture = texture; + this.updateFrame = true; +} + +/** + * When the texture is updated, this event will fire to update the frame + * + * @method onTextureUpdate + * @param event + * @private + */ +PIXI.TilingSprite.prototype.onTextureUpdate = function(event) +{ + this.updateFrame = true; +} + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi + * + * Awesome JS run time provided by EsotericSoftware + * https://github.com/EsotericSoftware/spine-runtimes + * + */ + +/** + * A class that enables the you to import and run your spine animations in pixi. + * Spine animation data needs to be loaded using the PIXI.AssetLoader or PIXI.SpineLoader before it can be used by this class + * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * + * @class Spine + * @extends DisplayObjectContainer + * @constructor + * @param url {String} The url of the spine anim file to be used + */ +PIXI.Spine = function (url) { + PIXI.DisplayObjectContainer.call(this); + + this.spineData = PIXI.AnimCache[url]; + + if (!this.spineData) { + throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); + } + + this.skeleton = new spine.Skeleton(this.spineData); + this.skeleton.updateWorldTransform(); + + this.stateData = new spine.AnimationStateData(this.spineData); + this.state = new spine.AnimationState(this.stateData); + + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +PIXI.Spine.prototype.constructor = PIXI.Spine; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); + this.state.apply(this.skeleton); + this.skeleton.updateWorldTransform(); + + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } + + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; + } + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; + } + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; + +/* + * Awesome JS run time provided by EsotericSoftware + * + * https://github.com/EsotericSoftware/spine-runtimes + * + */ + +var spine = {}; + +spine.BoneData = function (name, parent) { + this.name = name; + this.parent = parent; +}; +spine.BoneData.prototype = { + length: 0, + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1 +}; + +spine.SlotData = function (name, boneData) { + this.name = name; + this.boneData = boneData; +}; +spine.SlotData.prototype = { + r: 1, g: 1, b: 1, a: 1, + attachmentName: null +}; + +spine.Bone = function (boneData, parent) { + this.data = boneData; + this.parent = parent; + this.setToSetupPose(); +}; +spine.Bone.yDown = false; +spine.Bone.prototype = { + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1, + m00: 0, m01: 0, worldX: 0, // a b x + m10: 0, m11: 0, worldY: 0, // c d y + worldRotation: 0, + worldScaleX: 1, worldScaleY: 1, + updateWorldTransform: function (flipX, flipY) { + var parent = this.parent; + if (parent != null) { + this.worldX = this.x * parent.m00 + this.y * parent.m01 + parent.worldX; + this.worldY = this.x * parent.m10 + this.y * parent.m11 + parent.worldY; + this.worldScaleX = parent.worldScaleX * this.scaleX; + this.worldScaleY = parent.worldScaleY * this.scaleY; + this.worldRotation = parent.worldRotation + this.rotation; + } else { + this.worldX = this.x; + this.worldY = this.y; + this.worldScaleX = this.scaleX; + this.worldScaleY = this.scaleY; + this.worldRotation = this.rotation; + } + var radians = this.worldRotation * Math.PI / 180; + var cos = Math.cos(radians); + var sin = Math.sin(radians); + this.m00 = cos * this.worldScaleX; + this.m10 = sin * this.worldScaleX; + this.m01 = -sin * this.worldScaleY; + this.m11 = cos * this.worldScaleY; + if (flipX) { + this.m00 = -this.m00; + this.m01 = -this.m01; + } + if (flipY) { + this.m10 = -this.m10; + this.m11 = -this.m11; + } + if (spine.Bone.yDown) { + this.m10 = -this.m10; + this.m11 = -this.m11; + } + }, + setToSetupPose: function () { + var data = this.data; + this.x = data.x; + this.y = data.y; + this.rotation = data.rotation; + this.scaleX = data.scaleX; + this.scaleY = data.scaleY; + } +}; + +spine.Slot = function (slotData, skeleton, bone) { + this.data = slotData; + this.skeleton = skeleton; + this.bone = bone; + this.setToSetupPose(); +}; +spine.Slot.prototype = { + r: 1, g: 1, b: 1, a: 1, + _attachmentTime: 0, + attachment: null, + setAttachment: function (attachment) { + this.attachment = attachment; + this._attachmentTime = this.skeleton.time; + }, + setAttachmentTime: function (time) { + this._attachmentTime = this.skeleton.time - time; + }, + getAttachmentTime: function () { + return this.skeleton.time - this._attachmentTime; + }, + setToSetupPose: function () { + var data = this.data; + this.r = data.r; + this.g = data.g; + this.b = data.b; + this.a = data.a; + + var slotDatas = this.skeleton.data.slots; + for (var i = 0, n = slotDatas.length; i < n; i++) { + if (slotDatas[i] == data) { + this.setAttachment(!data.attachmentName ? null : this.skeleton.getAttachmentBySlotIndex(i, data.attachmentName)); + break; + } + } + } +}; + +spine.Skin = function (name) { + this.name = name; + this.attachments = {}; +}; +spine.Skin.prototype = { + addAttachment: function (slotIndex, name, attachment) { + this.attachments[slotIndex + ":" + name] = attachment; + }, + getAttachment: function (slotIndex, name) { + return this.attachments[slotIndex + ":" + name]; + }, + _attachAll: function (skeleton, oldSkin) { + for (var key in oldSkin.attachments) { + var colon = key.indexOf(":"); + var slotIndex = parseInt(key.substring(0, colon)); + var name = key.substring(colon + 1); + var slot = skeleton.slots[slotIndex]; + if (slot.attachment && slot.attachment.name == name) { + var attachment = this.getAttachment(slotIndex, name); + if (attachment) slot.setAttachment(attachment); + } + } + } +}; + +spine.Animation = function (name, timelines, duration) { + this.name = name; + this.timelines = timelines; + this.duration = duration; +}; +spine.Animation.prototype = { + apply: function (skeleton, time, loop) { + if (loop && this.duration != 0) time %= this.duration; + var timelines = this.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + timelines[i].apply(skeleton, time, 1); + }, + mix: function (skeleton, time, loop, alpha) { + if (loop && this.duration != 0) time %= this.duration; + var timelines = this.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + timelines[i].apply(skeleton, time, alpha); + } +}; + +spine.binarySearch = function (values, target, step) { + var low = 0; + var high = Math.floor(values.length / step) - 2; + if (high == 0) return step; + var current = high >>> 1; + while (true) { + if (values[(current + 1) * step] <= target) + low = current + 1; + else + high = current; + if (low == high) return (low + 1) * step; + current = (low + high) >>> 1; + } +}; +spine.linearSearch = function (values, target, step) { + for (var i = 0, last = values.length - step; i <= last; i += step) + if (values[i] > target) return i; + return -1; +}; + +spine.Curves = function (frameCount) { + this.curves = []; // dfx, dfy, ddfx, ddfy, dddfx, dddfy, ... + this.curves.length = (frameCount - 1) * 6; +}; +spine.Curves.prototype = { + setLinear: function (frameIndex) { + this.curves[frameIndex * 6] = 0/*LINEAR*/; + }, + setStepped: function (frameIndex) { + this.curves[frameIndex * 6] = -1/*STEPPED*/; + }, + /** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + * the difference between the keyframe's values. */ + setCurve: function (frameIndex, cx1, cy1, cx2, cy2) { + var subdiv_step = 1 / 10/*BEZIER_SEGMENTS*/; + var subdiv_step2 = subdiv_step * subdiv_step; + var subdiv_step3 = subdiv_step2 * subdiv_step; + var pre1 = 3 * subdiv_step; + var pre2 = 3 * subdiv_step2; + var pre4 = 6 * subdiv_step2; + var pre5 = 6 * subdiv_step3; + var tmp1x = -cx1 * 2 + cx2; + var tmp1y = -cy1 * 2 + cy2; + var tmp2x = (cx1 - cx2) * 3 + 1; + var tmp2y = (cy1 - cy2) * 3 + 1; + var i = frameIndex * 6; + var curves = this.curves; + curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3; + curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3; + curves[i + 2] = tmp1x * pre4 + tmp2x * pre5; + curves[i + 3] = tmp1y * pre4 + tmp2y * pre5; + curves[i + 4] = tmp2x * pre5; + curves[i + 5] = tmp2y * pre5; + }, + getCurvePercent: function (frameIndex, percent) { + percent = percent < 0 ? 0 : (percent > 1 ? 1 : percent); + var curveIndex = frameIndex * 6; + var curves = this.curves; + var dfx = curves[curveIndex]; + if (!dfx/*LINEAR*/) return percent; + if (dfx == -1/*STEPPED*/) return 0; + var dfy = curves[curveIndex + 1]; + var ddfx = curves[curveIndex + 2]; + var ddfy = curves[curveIndex + 3]; + var dddfx = curves[curveIndex + 4]; + var dddfy = curves[curveIndex + 5]; + var x = dfx, y = dfy; + var i = 10/*BEZIER_SEGMENTS*/ - 2; + while (true) { + if (x >= percent) { + var lastX = x - dfx; + var lastY = y - dfy; + return lastY + (y - lastY) * (percent - lastX) / (x - lastX); + } + if (i == 0) break; + i--; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } + return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + } +}; + +spine.RotateTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, angle, ... + this.frames.length = frameCount * 2; +}; +spine.RotateTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, angle) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = angle; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 2]) { // Time is after last frame. + var amount = bone.data.rotation + frames[frames.length - 1] - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 2); + var lastFrameValue = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 2/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 2 - 1, percent); + + var amount = frames[frameIndex + 1/*FRAME_VALUE*/] - lastFrameValue; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + amount = bone.data.rotation + (lastFrameValue + amount * percent) - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + } +}; + +spine.TranslateTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, x, y, ... + this.frames.length = frameCount * 3; +}; +spine.TranslateTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = x; + this.frames[frameIndex + 2] = y; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.x += (bone.data.x + frames[frames.length - 2] - bone.x) * alpha; + bone.y += (bone.data.y + frames[frames.length - 1] - bone.y) * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 3); + var lastFrameX = frames[frameIndex - 2]; + var lastFrameY = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; + bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; + } +}; + +spine.ScaleTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, x, y, ... + this.frames.length = frameCount * 3; +}; +spine.ScaleTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = x; + this.frames[frameIndex + 2] = y; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 3); + var lastFrameX = frames[frameIndex - 2]; + var lastFrameY = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + bone.scaleX += (bone.data.scaleX - 1 + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY - 1 + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.scaleY) * alpha; + } +}; + +spine.ColorTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, r, g, b, a, ... + this.frames.length = frameCount * 5; +}; +spine.ColorTimeline.prototype = { + slotIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 5; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = r; + this.frames[frameIndex + 2] = g; + this.frames[frameIndex + 3] = b; + this.frames[frameIndex + 4] = a; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var slot = skeleton.slots[this.slotIndex]; + + if (time >= frames[frames.length - 5]) { // Time is after last frame. + var i = frames.length - 1; + slot.r = frames[i - 3]; + slot.g = frames[i - 2]; + slot.b = frames[i - 1]; + slot.a = frames[i]; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 5); + var lastFrameR = frames[frameIndex - 4]; + var lastFrameG = frames[frameIndex - 3]; + var lastFrameB = frames[frameIndex - 2]; + var lastFrameA = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 5/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 5 - 1, percent); + + var r = lastFrameR + (frames[frameIndex + 1/*FRAME_R*/] - lastFrameR) * percent; + var g = lastFrameG + (frames[frameIndex + 2/*FRAME_G*/] - lastFrameG) * percent; + var b = lastFrameB + (frames[frameIndex + 3/*FRAME_B*/] - lastFrameB) * percent; + var a = lastFrameA + (frames[frameIndex + 4/*FRAME_A*/] - lastFrameA) * percent; + if (alpha < 1) { + slot.r += (r - slot.r) * alpha; + slot.g += (g - slot.g) * alpha; + slot.b += (b - slot.b) * alpha; + slot.a += (a - slot.a) * alpha; + } else { + slot.r = r; + slot.g = g; + slot.b = b; + slot.a = a; + } + } +}; + +spine.AttachmentTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, ... + this.frames.length = frameCount; + this.attachmentNames = []; // time, ... + this.attachmentNames.length = frameCount; +}; +spine.AttachmentTimeline.prototype = { + slotIndex: 0, + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, attachmentName) { + this.frames[frameIndex] = time; + this.attachmentNames[frameIndex] = attachmentName; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var frameIndex; + if (time >= frames[frames.length - 1]) // Time is after last frame. + frameIndex = frames.length - 1; + else + frameIndex = spine.binarySearch(frames, time, 1) - 1; + + var attachmentName = this.attachmentNames[frameIndex]; + skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); + } +}; + +spine.SkeletonData = function () { + this.bones = []; + this.slots = []; + this.skins = []; + this.animations = []; +}; +spine.SkeletonData.prototype = { + defaultSkin: null, + /** @return May be null. */ + findBone: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) return bones[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findBoneIndex: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) return i; + return -1; + }, + /** @return May be null. */ + findSlot: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + if (slots[i].name == slotName) return slot[i]; + } + return null; + }, + /** @return -1 if the bone was not found. */ + findSlotIndex: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].name == slotName) return i; + return -1; + }, + /** @return May be null. */ + findSkin: function (skinName) { + var skins = this.skins; + for (var i = 0, n = skins.length; i < n; i++) + if (skins[i].name == skinName) return skins[i]; + return null; + }, + /** @return May be null. */ + findAnimation: function (animationName) { + var animations = this.animations; + for (var i = 0, n = animations.length; i < n; i++) + if (animations[i].name == animationName) return animations[i]; + return null; + } +}; + +spine.Skeleton = function (skeletonData) { + this.data = skeletonData; + + this.bones = []; + for (var i = 0, n = skeletonData.bones.length; i < n; i++) { + var boneData = skeletonData.bones[i]; + var parent = !boneData.parent ? null : this.bones[skeletonData.bones.indexOf(boneData.parent)]; + this.bones.push(new spine.Bone(boneData, parent)); + } + + this.slots = []; + this.drawOrder = []; + for (var i = 0, n = skeletonData.slots.length; i < n; i++) { + var slotData = skeletonData.slots[i]; + var bone = this.bones[skeletonData.bones.indexOf(slotData.boneData)]; + var slot = new spine.Slot(slotData, this, bone); + this.slots.push(slot); + this.drawOrder.push(slot); + } +}; +spine.Skeleton.prototype = { + x: 0, y: 0, + skin: null, + r: 1, g: 1, b: 1, a: 1, + time: 0, + flipX: false, flipY: false, + /** Updates the world transform for each bone. */ + updateWorldTransform: function () { + var flipX = this.flipX; + var flipY = this.flipY; + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + bones[i].updateWorldTransform(flipX, flipY); + }, + /** Sets the bones and slots to their setup pose values. */ + setToSetupPose: function () { + this.setBonesToSetupPose(); + this.setSlotsToSetupPose(); + }, + setBonesToSetupPose: function () { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + bones[i].setToSetupPose(); + }, + setSlotsToSetupPose: function () { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + slots[i].setToSetupPose(i); + }, + /** @return May return null. */ + getRootBone: function () { + return this.bones.length == 0 ? null : this.bones[0]; + }, + /** @return May be null. */ + findBone: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) return bones[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findBoneIndex: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) return i; + return -1; + }, + /** @return May be null. */ + findSlot: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) return slots[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findSlotIndex: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) return i; + return -1; + }, + setSkinByName: function (skinName) { + var skin = this.data.findSkin(skinName); + if (!skin) throw "Skin not found: " + skinName; + this.setSkin(skin); + }, + /** Sets the skin used to look up attachments not found in the {@link SkeletonData#getDefaultSkin() default skin}. Attachments + * from the new skin are attached if the corresponding attachment from the old skin was attached. + * @param newSkin May be null. */ + setSkin: function (newSkin) { + if (this.skin && newSkin) newSkin._attachAll(this, this.skin); + this.skin = newSkin; + }, + /** @return May be null. */ + getAttachmentBySlotName: function (slotName, attachmentName) { + return this.getAttachmentBySlotIndex(this.data.findSlotIndex(slotName), attachmentName); + }, + /** @return May be null. */ + getAttachmentBySlotIndex: function (slotIndex, attachmentName) { + if (this.skin) { + var attachment = this.skin.getAttachment(slotIndex, attachmentName); + if (attachment) return attachment; + } + if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; + }, + /** @param attachmentName May be null. */ + setAttachment: function (slotName, attachmentName) { + var slots = this.slots; + for (var i = 0, n = slots.size; i < n; i++) { + var slot = slots[i]; + if (slot.data.name == slotName) { + var attachment = null; + if (attachmentName) { + attachment = this.getAttachment(i, attachmentName); + if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; + } + slot.setAttachment(attachment); + return; + } + } + throw "Slot not found: " + slotName; + }, + update: function (delta) { + time += delta; + } +}; + +spine.AttachmentType = { + region: 0 +}; + +spine.RegionAttachment = function () { + this.offset = []; + this.offset.length = 8; + this.uvs = []; + this.uvs.length = 8; +}; +spine.RegionAttachment.prototype = { + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1, + width: 0, height: 0, + rendererObject: null, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + setUVs: function (u, v, u2, v2, rotate) { + var uvs = this.uvs; + if (rotate) { + uvs[2/*X2*/] = u; + uvs[3/*Y2*/] = v2; + uvs[4/*X3*/] = u; + uvs[5/*Y3*/] = v; + uvs[6/*X4*/] = u2; + uvs[7/*Y4*/] = v; + uvs[0/*X1*/] = u2; + uvs[1/*Y1*/] = v2; + } else { + uvs[0/*X1*/] = u; + uvs[1/*Y1*/] = v2; + uvs[2/*X2*/] = u; + uvs[3/*Y2*/] = v; + uvs[4/*X3*/] = u2; + uvs[5/*Y3*/] = v; + uvs[6/*X4*/] = u2; + uvs[7/*Y4*/] = v2; + } + }, + updateOffset: function () { + var regionScaleX = this.width / this.regionOriginalWidth * this.scaleX; + var regionScaleY = this.height / this.regionOriginalHeight * this.scaleY; + var localX = -this.width / 2 * this.scaleX + this.regionOffsetX * regionScaleX; + var localY = -this.height / 2 * this.scaleY + this.regionOffsetY * regionScaleY; + var localX2 = localX + this.regionWidth * regionScaleX; + var localY2 = localY + this.regionHeight * regionScaleY; + var radians = this.rotation * Math.PI / 180; + var cos = Math.cos(radians); + var sin = Math.sin(radians); + var localXCos = localX * cos + this.x; + var localXSin = localX * sin; + var localYCos = localY * cos + this.y; + var localYSin = localY * sin; + var localX2Cos = localX2 * cos + this.x; + var localX2Sin = localX2 * sin; + var localY2Cos = localY2 * cos + this.y; + var localY2Sin = localY2 * sin; + var offset = this.offset; + offset[0/*X1*/] = localXCos - localYSin; + offset[1/*Y1*/] = localYCos + localXSin; + offset[2/*X2*/] = localXCos - localY2Sin; + offset[3/*Y2*/] = localY2Cos + localXSin; + offset[4/*X3*/] = localX2Cos - localY2Sin; + offset[5/*Y3*/] = localY2Cos + localX2Sin; + offset[6/*X4*/] = localX2Cos - localYSin; + offset[7/*Y4*/] = localYCos + localX2Sin; + }, + computeVertices: function (x, y, bone, vertices) { + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00; + var m01 = bone.m01; + var m10 = bone.m10; + var m11 = bone.m11; + var offset = this.offset; + vertices[0/*X1*/] = offset[0/*X1*/] * m00 + offset[1/*Y1*/] * m01 + x; + vertices[1/*Y1*/] = offset[0/*X1*/] * m10 + offset[1/*Y1*/] * m11 + y; + vertices[2/*X2*/] = offset[2/*X2*/] * m00 + offset[3/*Y2*/] * m01 + x; + vertices[3/*Y2*/] = offset[2/*X2*/] * m10 + offset[3/*Y2*/] * m11 + y; + vertices[4/*X3*/] = offset[4/*X3*/] * m00 + offset[5/*X3*/] * m01 + x; + vertices[5/*X3*/] = offset[4/*X3*/] * m10 + offset[5/*X3*/] * m11 + y; + vertices[6/*X4*/] = offset[6/*X4*/] * m00 + offset[7/*Y4*/] * m01 + x; + vertices[7/*Y4*/] = offset[6/*X4*/] * m10 + offset[7/*Y4*/] * m11 + y; + } +} + +spine.AnimationStateData = function (skeletonData) { + this.skeletonData = skeletonData; + this.animationToMixTime = {}; +}; +spine.AnimationStateData.prototype = { + defaultMix: 0, + setMixByName: function (fromName, toName, duration) { + var from = this.skeletonData.findAnimation(fromName); + if (!from) throw "Animation not found: " + fromName; + var to = this.skeletonData.findAnimation(toName); + if (!to) throw "Animation not found: " + toName; + this.setMix(from, to, duration); + }, + setMix: function (from, to, duration) { + this.animationToMixTime[from.name + ":" + to.name] = duration; + }, + getMix: function (from, to) { + var time = this.animationToMixTime[from.name + ":" + to.name]; + return time ? time : this.defaultMix; + } +}; + +spine.AnimationState = function (stateData) { + this.data = stateData; + this.queue = []; +}; +spine.AnimationState.prototype = { + current: null, + previous: null, + currentTime: 0, + previousTime: 0, + currentLoop: false, + previousLoop: false, + mixTime: 0, + mixDuration: 0, + update: function (delta) { + this.currentTime += delta; + this.previousTime += delta; + this.mixTime += delta; + + if (this.queue.length > 0) { + var entry = this.queue[0]; + if (this.currentTime >= entry.delay) { + this._setAnimation(entry.animation, entry.loop); + this.queue.shift(); + } + } + }, + apply: function (skeleton) { + if (!this.current) return; + if (this.previous) { + this.previous.apply(skeleton, this.previousTime, this.previousLoop); + var alpha = this.mixTime / this.mixDuration; + if (alpha >= 1) { + alpha = 1; + this.previous = null; + } + this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); + } else + this.current.apply(skeleton, this.currentTime, this.currentLoop); + }, + clearAnimation: function () { + this.previous = null; + this.current = null; + this.queue.length = 0; + }, + _setAnimation: function (animation, loop) { + this.previous = null; + if (animation && this.current) { + this.mixDuration = this.data.getMix(this.current, animation); + if (this.mixDuration > 0) { + this.mixTime = 0; + this.previous = this.current; + this.previousTime = this.currentTime; + this.previousLoop = this.currentLoop; + } + } + this.current = animation; + this.currentLoop = loop; + this.currentTime = 0; + }, + /** @see #setAnimation(Animation, Boolean) */ + setAnimationByName: function (animationName, loop) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (!animation) throw "Animation not found: " + animationName; + this.setAnimation(animation, loop); + }, + /** Set the current animation. Any queued animations are cleared and the current animation time is set to 0. + * @param animation May be null. */ + setAnimation: function (animation, loop) { + this.queue.length = 0; + this._setAnimation(animation, loop); + }, + /** @see #addAnimation(Animation, Boolean, Number) */ + addAnimationByName: function (animationName, loop, delay) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (!animation) throw "Animation not found: " + animationName; + this.addAnimation(animation, loop, delay); + }, + /** Adds an animation to be played delay seconds after the current or last queued animation. + * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ + addAnimation: function (animation, loop, delay) { + var entry = {}; + entry.animation = animation; + entry.loop = loop; + + if (!delay || delay <= 0) { + var previousAnimation = this.queue.length == 0 ? this.current : this.queue[this.queue.length - 1].animation; + if (previousAnimation != null) + delay = previousAnimation.duration - this.data.getMix(previousAnimation, animation) + (delay || 0); + else + delay = 0; + } + entry.delay = delay; + + this.queue.push(entry); + }, + /** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */ + isComplete: function () { + return !this.current || this.currentTime >= this.current.duration; + } +}; + +spine.SkeletonJson = function (attachmentLoader) { + this.attachmentLoader = attachmentLoader; +}; +spine.SkeletonJson.prototype = { + scale: 1, + readSkeletonData: function (root) { + var skeletonData = new spine.SkeletonData(); + + // Bones. + var bones = root["bones"]; + for (var i = 0, n = bones.length; i < n; i++) { + var boneMap = bones[i]; + var parent = null; + if (boneMap["parent"]) { + parent = skeletonData.findBone(boneMap["parent"]); + if (!parent) throw "Parent bone not found: " + boneMap["parent"]; + } + var boneData = new spine.BoneData(boneMap["name"], parent); + boneData.length = (boneMap["length"] || 0) * this.scale; + boneData.x = (boneMap["x"] || 0) * this.scale; + boneData.y = (boneMap["y"] || 0) * this.scale; + boneData.rotation = (boneMap["rotation"] || 0); + boneData.scaleX = boneMap["scaleX"] || 1; + boneData.scaleY = boneMap["scaleY"] || 1; + skeletonData.bones.push(boneData); + } + + // Slots. + var slots = root["slots"]; + for (var i = 0, n = slots.length; i < n; i++) { + var slotMap = slots[i]; + var boneData = skeletonData.findBone(slotMap["bone"]); + if (!boneData) throw "Slot bone not found: " + slotMap["bone"]; + var slotData = new spine.SlotData(slotMap["name"], boneData); + + var color = slotMap["color"]; + if (color) { + slotData.r = spine.SkeletonJson.toColor(color, 0); + slotData.g = spine.SkeletonJson.toColor(color, 1); + slotData.b = spine.SkeletonJson.toColor(color, 2); + slotData.a = spine.SkeletonJson.toColor(color, 3); + } + + slotData.attachmentName = slotMap["attachment"]; + + skeletonData.slots.push(slotData); + } + + // Skins. + var skins = root["skins"]; + for (var skinName in skins) { + if (!skins.hasOwnProperty(skinName)) continue; + var skinMap = skins[skinName]; + var skin = new spine.Skin(skinName); + for (var slotName in skinMap) { + if (!skinMap.hasOwnProperty(slotName)) continue; + var slotIndex = skeletonData.findSlotIndex(slotName); + var slotEntry = skinMap[slotName]; + for (var attachmentName in slotEntry) { + if (!slotEntry.hasOwnProperty(attachmentName)) continue; + var attachment = this.readAttachment(skin, attachmentName, slotEntry[attachmentName]); + if (attachment != null) skin.addAttachment(slotIndex, attachmentName, attachment); + } + } + skeletonData.skins.push(skin); + if (skin.name == "default") skeletonData.defaultSkin = skin; + } + + // Animations. + var animations = root["animations"]; + for (var animationName in animations) { + if (!animations.hasOwnProperty(animationName)) continue; + this.readAnimation(animationName, animations[animationName], skeletonData); + } + + return skeletonData; + }, + readAttachment: function (skin, name, map) { + name = map["name"] || name; + + var type = spine.AttachmentType[map["type"] || "region"]; + + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); + attachment.x = (map["x"] || 0) * this.scale; + attachment.y = (map["y"] || 0) * this.scale; + attachment.scaleX = map["scaleX"] || 1; + attachment.scaleY = map["scaleY"] || 1; + attachment.rotation = map["rotation"] || 0; + attachment.width = (map["width"] || 32) * this.scale; + attachment.height = (map["height"] || 32) * this.scale; + attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; + } + + throw "Unknown attachment type: " + type; + }, + + readAnimation: function (name, map, skeletonData) { + var timelines = []; + var duration = 0; + + var bones = map["bones"]; + for (var boneName in bones) { + if (!bones.hasOwnProperty(boneName)) continue; + var boneIndex = skeletonData.findBoneIndex(boneName); + if (boneIndex == -1) throw "Bone not found: " + boneName; + var boneMap = bones[boneName]; + + for (var timelineName in boneMap) { + if (!boneMap.hasOwnProperty(timelineName)) continue; + var values = boneMap[timelineName]; + if (timelineName == "rotate") { + var timeline = new spine.RotateTimeline(values.length); + timeline.boneIndex = boneIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + timeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]); + spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); + + } else if (timelineName == "translate" || timelineName == "scale") { + var timeline; + var timelineScale = 1; + if (timelineName == "scale") + timeline = new spine.ScaleTimeline(values.length); + else { + timeline = new spine.TranslateTimeline(values.length); + timelineScale = this.scale; + } + timeline.boneIndex = boneIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var x = (valueMap["x"] || 0) * timelineScale; + var y = (valueMap["y"] || 0) * timelineScale; + timeline.setFrame(frameIndex, valueMap["time"], x, y); + spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); + + } else + throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; + } + } + var slots = map["slots"]; + for (var slotName in slots) { + if (!slots.hasOwnProperty(slotName)) continue; + var slotMap = slots[slotName]; + var slotIndex = skeletonData.findSlotIndex(slotName); + + for (var timelineName in slotMap) { + if (!slotMap.hasOwnProperty(timelineName)) continue; + var values = slotMap[timelineName]; + if (timelineName == "color") { + var timeline = new spine.ColorTimeline(values.length); + timeline.slotIndex = slotIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var color = valueMap["color"]; + var r = spine.SkeletonJson.toColor(color, 0); + var g = spine.SkeletonJson.toColor(color, 1); + var b = spine.SkeletonJson.toColor(color, 2); + var a = spine.SkeletonJson.toColor(color, 3); + timeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); + spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 5 - 5]); + + } else if (timelineName == "attachment") { + var timeline = new spine.AttachmentTimeline(values.length); + timeline.slotIndex = slotIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + + } else + throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; + } + } + skeletonData.animations.push(new spine.Animation(name, timelines, duration)); + } +}; +spine.SkeletonJson.readCurve = function (timeline, frameIndex, valueMap) { + var curve = valueMap["curve"]; + if (!curve) return; + if (curve == "stepped") + timeline.curves.setStepped(frameIndex); + else if (curve instanceof Array) + timeline.curves.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); +}; +spine.SkeletonJson.toColor = function (hexString, colorIndex) { + if (hexString.length != 8) throw "Color hexidecimal length must be 8, recieved: " + hexString; + return parseInt(hexString.substring(colorIndex * 2, 2), 16) / 255; +}; + +spine.Atlas = function (atlasText, textureLoader) { + this.textureLoader = textureLoader; + this.pages = []; + this.regions = []; + + var reader = new spine.AtlasReader(atlasText); + var tuple = []; + tuple.length = 4; + var page = null; + while (true) { + var line = reader.readLine(); + if (line == null) break; + line = reader.trim(line); + if (line.length == 0) + page = null; + else if (!page) { + page = new spine.AtlasPage(); + page.name = line; + + page.format = spine.Atlas.Format[reader.readValue()]; + + reader.readTuple(tuple); + page.minFilter = spine.Atlas.TextureFilter[tuple[0]]; + page.magFilter = spine.Atlas.TextureFilter[tuple[1]]; + + var direction = reader.readValue(); + page.uWrap = spine.Atlas.TextureWrap.clampToEdge; + page.vWrap = spine.Atlas.TextureWrap.clampToEdge; + if (direction == "x") + page.uWrap = spine.Atlas.TextureWrap.repeat; + else if (direction == "y") + page.vWrap = spine.Atlas.TextureWrap.repeat; + else if (direction == "xy") + page.uWrap = page.vWrap = spine.Atlas.TextureWrap.repeat; + + textureLoader.load(page, line); + + this.pages.push(page); + + } else { + var region = new spine.AtlasRegion(); + region.name = line; + region.page = page; + + region.rotate = reader.readValue() == "true"; + + reader.readTuple(tuple); + var x = parseInt(tuple[0]); + var y = parseInt(tuple[1]); + + reader.readTuple(tuple); + var width = parseInt(tuple[0]); + var height = parseInt(tuple[1]); + + region.u = x / page.width; + region.v = y / page.height; + if (region.rotate) { + region.u2 = (x + height) / page.width; + region.v2 = (y + width) / page.height; + } else { + region.u2 = (x + width) / page.width; + region.v2 = (y + height) / page.height; + } + region.x = x; + region.y = y; + region.width = Math.abs(width); + region.height = Math.abs(height); + + if (reader.readTuple(tuple) == 4) { // split is optional + region.splits = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])]; + + if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits + region.pads = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])]; + + reader.readTuple(tuple); + } + } + + region.originalWidth = parseInt(tuple[0]); + region.originalHeight = parseInt(tuple[1]); + + reader.readTuple(tuple); + region.offsetX = parseInt(tuple[0]); + region.offsetY = parseInt(tuple[1]); + + region.index = parseInt(reader.readValue()); + + this.regions.push(region); + } + } +}; +spine.Atlas.prototype = { + findRegion: function (name) { + var regions = this.regions; + for (var i = 0, n = regions.length; i < n; i++) + if (regions[i].name == name) return regions[i]; + return null; + }, + dispose: function () { + var pages = this.pages; + for (var i = 0, n = pages.length; i < n; i++) + this.textureLoader.unload(pages[i].rendererObject); + }, + updateUVs: function (page) { + var regions = this.regions; + for (var i = 0, n = regions.length; i < n; i++) { + var region = regions[i]; + if (region.page != page) continue; + region.u = region.x / page.width; + region.v = region.y / page.height; + if (region.rotate) { + region.u2 = (region.x + region.height) / page.width; + region.v2 = (region.y + region.width) / page.height; + } else { + region.u2 = (region.x + region.width) / page.width; + region.v2 = (region.y + region.height) / page.height; + } + } + } +}; + +spine.Atlas.Format = { + alpha: 0, + intensity: 1, + luminanceAlpha: 2, + rgb565: 3, + rgba4444: 4, + rgb888: 5, + rgba8888: 6 +}; + +spine.Atlas.TextureFilter = { + nearest: 0, + linear: 1, + mipMap: 2, + mipMapNearestNearest: 3, + mipMapLinearNearest: 4, + mipMapNearestLinear: 5, + mipMapLinearLinear: 6 +}; + +spine.Atlas.TextureWrap = { + mirroredRepeat: 0, + clampToEdge: 1, + repeat: 2 +}; + +spine.AtlasPage = function () {}; +spine.AtlasPage.prototype = { + name: null, + format: null, + minFilter: null, + magFilter: null, + uWrap: null, + vWrap: null, + rendererObject: null, + width: 0, + height: 0 +}; + +spine.AtlasRegion = function () {}; +spine.AtlasRegion.prototype = { + page: null, + name: null, + x: 0, y: 0, + width: 0, height: 0, + u: 0, v: 0, u2: 0, v2: 0, + offsetX: 0, offsetY: 0, + originalWidth: 0, originalHeight: 0, + index: 0, + rotate: false, + splits: null, + pads: null, +}; + +spine.AtlasReader = function (text) { + this.lines = text.split(/\r\n|\r|\n/); +}; +spine.AtlasReader.prototype = { + index: 0, + trim: function (value) { + return value.replace(/^\s+|\s+$/g, ""); + }, + readLine: function () { + if (this.index >= this.lines.length) return null; + return this.lines[this.index++]; + }, + readValue: function () { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) throw "Invalid line: " + line; + return this.trim(line.substring(colon + 1)); + }, + /** Returns the number of tuple values read (2 or 4). */ + readTuple: function (tuple) { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) throw "Invalid line: " + line; + var i = 0, lastMatch= colon + 1; + for (; i < 3; i++) { + var comma = line.indexOf(",", lastMatch); + if (comma == -1) { + if (i == 0) throw "Invalid line: " + line; + break; + } + tuple[i] = this.trim(line.substr(lastMatch, comma - lastMatch)); + lastMatch = comma + 1; + } + tuple[i] = this.trim(line.substring(lastMatch)); + return i + 1; + } +} + +spine.AtlasAttachmentLoader = function (atlas) { + this.atlas = atlas; +} +spine.AtlasAttachmentLoader.prototype = { + newAttachment: function (skin, type, name) { + switch (type) { + case spine.AttachmentType.region: + var region = this.atlas.findRegion(name); + if (!region) throw "Region not found in atlas: " + name + " (" + type + ")"; + var attachment = new spine.RegionAttachment(name); + attachment.rendererObject = region; + attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + } + throw "Unknown attachment type: " + type; + } +} + +PIXI.AnimCache = {}; +spine.Bone.yDown = true; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * This object is one that will allow you to specify custom rendering functions based on render type + * + * @class CustomRenderable + * @extends DisplayObject + * @constructor + */ +PIXI.CustomRenderable = function() +{ + PIXI.DisplayObject.call( this ); + +} + +// constructor +PIXI.CustomRenderable.prototype = Object.create( PIXI.DisplayObject.prototype ); +PIXI.CustomRenderable.prototype.constructor = PIXI.CustomRenderable; + +/** + * If this object is being rendered by a CanvasRenderer it will call this callback + * + * @method renderCanvas + * @param renderer {CanvasRenderer} The renderer instance + */ +PIXI.CustomRenderable.prototype.renderCanvas = function(renderer) +{ + // override! +} + +/** + * If this object is being rendered by a WebGLRenderer it will call this callback to initialize + * + * @method initWebGL + * @param renderer {WebGLRenderer} The renderer instance + */ +PIXI.CustomRenderable.prototype.initWebGL = function(renderer) +{ + // override! +} + +/** + * If this object is being rendered by a WebGLRenderer it will call this callback + * + * @method renderWebGL + * @param renderer {WebGLRenderer} The renderer instance + */ +PIXI.CustomRenderable.prototype.renderWebGL = function(renderGroup, projectionMatrix) +{ + // not sure if both needed? but ya have for now! + // override! +} + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.BaseTextureCache = {}; +PIXI.texturesToUpdate = []; +PIXI.texturesToDestroy = []; + +/** + * A texture stores the information that represents an image. All textures have a base texture + * + * @class BaseTexture + * @uses EventTarget + * @constructor + * @param source {String} the source object (image or canvas) + */ +PIXI.BaseTexture = function(source) +{ + PIXI.EventTarget.call( this ); + + /** + * [read-only] The width of the base texture set when the image has loaded + * + * @property width + * @type Number + * @readOnly + */ + this.width = 100; + + /** + * [read-only] The height of the base texture set when the image has loaded + * + * @property height + * @type Number + * @readOnly + */ + this.height = 100; + + /** + * [read-only] Describes if the base texture has loaded or not + * + * @property hasLoaded + * @type Boolean + * @readOnly + */ + this.hasLoaded = false; + + /** + * The source that is loaded to create the texture + * + * @property source + * @type Image + */ + this.source = source; + + 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 + { + this.hasLoaded = true; + this.width = this.source.width; + this.height = this.source.height; + + PIXI.texturesToUpdate.push(this); + } + + this._powerOf2 = false; +} + +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; + +/** + * Destroys this base texture + * + * @method destroy + */ +PIXI.BaseTexture.prototype.destroy = function() +{ + if(this.source instanceof Image) + { + this.source.src = null; + } + this.source = null; + PIXI.texturesToDestroy.push(this); +} + +/** + * 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 + * + * @static + * @method fromImage + * @param imageUrl {String} The image url of the texture + * @return BaseTexture + */ +PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) +{ + var baseTexture = PIXI.BaseTextureCache[imageUrl]; + if(!baseTexture) + { + // new Image() breaks tex loading in some versions of Chrome. + // See https://code.google.com/p/chromium/issues/detail?id=238071 + var image = new Image();//document.createElement('img'); + if (crossorigin) + { + image.crossOrigin = ''; + } + image.src = imageUrl; + baseTexture = new PIXI.BaseTexture(image); + PIXI.BaseTextureCache[imageUrl] = baseTexture; + } + + return baseTexture; +} + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.TextureCache = {}; +PIXI.FrameCache = {}; + +/** + * A texture stores the information that represents an image or part of an image. It cannot be added + * to the display list directly. To do this use PIXI.Sprite. If no frame is provided then the whole image is used + * + * @class Texture + * @uses EventTarget + * @constructor + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frame {Rectangle} The rectangle frame of the texture to show + */ +PIXI.Texture = function(baseTexture, frame) +{ + PIXI.EventTarget.call( this ); + + if(!frame) + { + this.noFrame = true; + frame = new PIXI.Rectangle(0,0,1,1); + } + + if(baseTexture instanceof PIXI.Texture) + baseTexture = baseTexture.baseTexture; + + /** + * The base texture of this texture + * + * @property baseTexture + * @type BaseTexture + */ + this.baseTexture = baseTexture; + + /** + * The frame specifies the region of the base texture that this texture uses + * + * @property frame + * @type Rectangle + */ + this.frame = frame; + + /** + * The trim point + * + * @property trim + * @type Point + */ + this.trim = new PIXI.Point(); + + this.scope = this; + + if(baseTexture.hasLoaded) + { + if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); + //console.log(frame) + + this.setFrame(frame); + } + else + { + var scope = this; + baseTexture.addEventListener( 'loaded', function(){ scope.onBaseTextureLoaded()} ); + } +} + +PIXI.Texture.prototype.constructor = PIXI.Texture; + +/** + * Called when the base texture is loaded + * + * @method onBaseTextureLoaded + * @param event + * @private + */ +PIXI.Texture.prototype.onBaseTextureLoaded = function(event) +{ + var baseTexture = this.baseTexture; + baseTexture.removeEventListener( 'loaded', this.onLoaded ); + + if(this.noFrame)this.frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); + this.noFrame = false; + this.width = this.frame.width; + this.height = this.frame.height; + + this.scope.dispatchEvent( { type: 'update', content: this } ); +} + +/** + * Destroys this texture + * + * @method destroy + * @param destroyBase {Boolean} Whether to destroy the base texture as well + */ +PIXI.Texture.prototype.destroy = function(destroyBase) +{ + if(destroyBase)this.baseTexture.destroy(); +} + +/** + * Specifies the rectangle region of the baseTexture + * + * @method setFrame + * @param frame {Rectangle} The frame of the texture to set it to + */ +PIXI.Texture.prototype.setFrame = function(frame) +{ + this.frame = frame; + this.width = frame.width; + this.height = frame.height; + + if(frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + { + throw new Error("Texture Error: frame does not fit inside the base Texture dimensions " + this); + } + + this.updateFrame = true; + + PIXI.Texture.frameUpdates.push(this); + //this.dispatchEvent( { type: 'update', content: this } ); +} + +/** + * Helper function that returns a texture based on an image url + * If the image is not in the texture cache it will be created and loaded + * + * @static + * @method fromImage + * @param imageUrl {String} The image url of the texture + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + * @return Texture + */ +PIXI.Texture.fromImage = function(imageUrl, crossorigin) +{ + var texture = PIXI.TextureCache[imageUrl]; + + if(!texture) + { + texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); + PIXI.TextureCache[imageUrl] = texture; + } + + return texture; +} + +/** + * Helper function that returns a texture based on a frame id + * If the frame id is not in the texture cache an error will be thrown + * + * @static + * @method fromFrame + * @param frameId {String} The frame id of the texture + * @return Texture + */ +PIXI.Texture.fromFrame = function(frameId) +{ + var texture = PIXI.TextureCache[frameId]; + if(!texture)throw new Error("The frameId '"+ frameId +"' does not exist in the texture cache " + this); + return texture; +} + +/** + * Helper function that returns a texture based on a canvas element + * If the canvas is not in the texture cache it will be created and loaded + * + * @static + * @method fromCanvas + * @param canvas {Canvas} The canvas element source of the texture + * @return Texture + */ +PIXI.Texture.fromCanvas = function(canvas) +{ + var baseTexture = new PIXI.BaseTexture(canvas); + return new PIXI.Texture(baseTexture); +} + + +/** + * Adds a texture to the textureCache. + * + * @static + * @method addTextureToCache + * @param texture {Texture} + * @param id {String} the id that the texture will be stored against. + */ +PIXI.Texture.addTextureToCache = function(texture, id) +{ + PIXI.TextureCache[id] = texture; +} + +/** + * Remove a texture from the textureCache. + * + * @static + * @method removeTextureFromCache + * @param id {String} the id of the texture to be removed + * @return {Texture} the texture that was removed + */ +PIXI.Texture.removeTextureFromCache = function(id) +{ + var texture = PIXI.TextureCache[id] + PIXI.TextureCache[id] = null; + return texture; +} + +// this is more for webGL.. it contains updated frames.. +PIXI.Texture.frameUpdates = []; + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + 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. + 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: + + var renderTexture = new PIXI.RenderTexture(800, 600); + var sprite = PIXI.Sprite.fromImage("spinObj_01.png"); + sprite.position.x = 800/2; + sprite.position.y = 600/2; + sprite.anchor.x = 0.5; + sprite.anchor.y = 0.5; + renderTexture.render(sprite); + + Sprite in this case will be rendered to 0,0 position. To render this sprite at center DisplayObjectContainer should be used: + + var doc = new PIXI.DisplayObjectContainer(); + doc.addChild(sprite); + renderTexture.render(doc); // Renders to center of renderTexture + + @class RenderTexture + @extends Texture + @constructor + @param width {Number} The width of the render texture + @param height {Number} The height of the render texture + */ +PIXI.RenderTexture = function(width, height) +{ + 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.initWebGL(); + } + else + { + this.initCanvas(); + } +} + +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.projection.x = this.width/2 + this.projection.y = this.height/2; + + var gl = PIXI.gl; + 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); + } + else + { + + this.frame.width = this.width + this.frame.height = this.height; + this.renderer.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; +} + +/** + * This function will draw the display object to the texture. + * + * @method renderWebGL + * @param displayObject {DisplayObject} The display object to render this texture on + * @param clear {Boolean} If true the texture will be cleared before the displayObject is drawn + * @private + */ +PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, clear) +{ + var gl = PIXI.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 ); + + if(clear) + { + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + + // 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; + // modify to flip... + displayObject.worldTransform[4] = -1; + displayObject.worldTransform[5] = this.projection.y * 2; + + + if(position) + { + displayObject.worldTransform[2] = position.x; + displayObject.worldTransform[5] -= position.y; + } + + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + + for(var i=0,j=children.length; i} assetURLs an array of image/sprite sheet urls that you would like loaded + * supported. Supported image formats include "jpeg", "jpg", "png", "gif". Supported + * sprite sheet data formats only include "JSON" at this time. Supported bitmap font + * data formats include "xml" and "fnt". + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.AssetLoader = function(assetURLs, crossorigin) +{ + PIXI.EventTarget.call(this); + + /** + * The array of asset URLs that are going to be loaded + * + * @property assetURLs + * @type Array + */ + this.assetURLs = assetURLs; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * Maps file extension to loader types + * + * @property loadersByType + * @type Object + */ + this.loadersByType = { + "jpg": PIXI.ImageLoader, + "jpeg": PIXI.ImageLoader, + "png": PIXI.ImageLoader, + "gif": PIXI.ImageLoader, + "json": PIXI.JsonLoader, + "anim": PIXI.SpineLoader, + "xml": PIXI.BitmapFontLoader, + "fnt": PIXI.BitmapFontLoader + }; + + +}; + +/** + * Fired when an item has loaded + * @event onProgress + */ + +/** + * Fired when all the assets have loaded + * @event onComplete + */ + +// constructor +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; + +/** + * Starts loading the assets sequentially + * + * @method load + */ +PIXI.AssetLoader.prototype.load = function() +{ + var scope = this; + + this.loadCount = this.assetURLs.length; + + for (var i=0; i < this.assetURLs.length; i++) + { + var fileName = this.assetURLs[i]; + var fileType = fileName.split(".").pop().toLowerCase(); + + var loaderClass = this.loadersByType[fileType]; + if(!loaderClass) + throw new Error(fileType + " is an unsupported file type"); + + var loader = new loaderClass(fileName, this.crossorigin); + + loader.addEventListener("loaded", function() + { + scope.onAssetLoaded(); + }); + loader.load(); + } +}; + +/** + * Invoked after each file is loaded + * + * @method onAssetLoaded + * @private + */ +PIXI.AssetLoader.prototype.onAssetLoaded = function() +{ + this.loadCount--; + this.dispatchEvent({type: "onProgress", content: this}); + if(this.onProgress) this.onProgress(); + + if(this.loadCount == 0) + { + this.dispatchEvent({type: "onComplete", content: this}); + if(this.onComplete) this.onComplete(); + } +}; + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The json file loader is used to load in JSON data and parsing it + * When loaded this class will dispatch a "loaded" event + * If load failed this class will dispatch a "error" event + * + * @class JsonLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.JsonLoader = function (url, crossorigin) { + PIXI.EventTarget.call(this); + + /** + * The url of the bitmap font data + * + * @property url + * @type String + */ + this.url = url; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * [read-only] The base url of the bitmap font data + * + * @property baseUrl + * @type String + * @readOnly + */ + this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * [read-only] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ + this.loaded = false; + +}; + +// constructor +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; + +/** + * Loads the JSON data + * + * @method load + */ +PIXI.JsonLoader.prototype.load = function () { + this.ajaxRequest = new AjaxRequest(); + var scope = this; + this.ajaxRequest.onreadystatechange = function () { + scope.onJSONLoaded(); + }; + + this.ajaxRequest.open("GET", this.url, true); + if (this.ajaxRequest.overrideMimeType) this.ajaxRequest.overrideMimeType("application/json"); + this.ajaxRequest.send(null); +}; + +/** + * Invoke when JSON file is loaded + * + * @method onJSONLoaded + * @private + */ +PIXI.JsonLoader.prototype.onJSONLoaded = function () { + if (this.ajaxRequest.readyState == 4) { + if (this.ajaxRequest.status == 200 || window.location.href.indexOf("http") == -1) { + this.json = JSON.parse(this.ajaxRequest.responseText); + + if(this.json.frames) + { + // sprite sheet + 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 (event) { + 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(); + + } + else if(this.json.bones) + { + // spine animation + var spineJsonParser = new spine.SkeletonJson(); + var skeletonData = spineJsonParser.readSkeletonData(this.json); + PIXI.AnimCache[this.url] = skeletonData; + this.onLoaded(); + } + else + { + this.onLoaded(); + } + } + else + { + this.onError(); + } + } +}; + +/** + * Invoke when json file loaded + * + * @method onLoaded + * @private + */ +PIXI.JsonLoader.prototype.onLoaded = function () { + this.loaded = true; + this.dispatchEvent({ + type: "loaded", + content: this + }); +}; + +/** + * Invoke when error occured + * + * @method onError + * @private + */ +PIXI.JsonLoader.prototype.onError = function () { + this.dispatchEvent({ + type: "error", + content: this + }); +}; +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * 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 + * 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. + * When loaded this class will dispatch a "loaded" event + * + * @class SpriteSheetLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ + +PIXI.SpriteSheetLoader = function (url, crossorigin) { + /* + * i use texture packer to load the assets.. + * http://www.codeandweb.com/texturepacker + * make sure to set the format as "JSON" + */ + PIXI.EventTarget.call(this); + + /** + * The url of the bitmap font data + * + * @property url + * @type String + */ + this.url = url; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * [read-only] The base url of the bitmap font data + * + * @property baseUrl + * @type String + * @readOnly + */ + this.baseUrl = url.replace(/[^\/]*$/, ""); + + /** + * The texture being loaded + * + * @property texture + * @type Texture + */ + this.texture = null; + + /** + * The frames of the sprite sheet + * + * @property frames + * @type Object + */ + this.frames = {}; +}; + +// constructor +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; + +/** + * This will begin loading the JSON file + * + * @method load + */ +PIXI.SpriteSheetLoader.prototype.load = function () { + var scope = this; + 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 (event) { + 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(); +}; +/** + * Invoke when all files are loaded (json and texture) + * + * @method onLoaded + * @private + */ +PIXI.SpriteSheetLoader.prototype.onLoaded = function () { + this.dispatchEvent({ + type: "loaded", + content: this + }); +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * 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() + * When loaded this class will dispatch a 'loaded' event + * + * @class ImageLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the image + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.ImageLoader = function(url, crossorigin) +{ + PIXI.EventTarget.call(this); + + /** + * The texture being loaded + * + * @property texture + * @type Texture + */ + this.texture = PIXI.Texture.fromImage(url, crossorigin); + + /** + * if the image is loaded with loadFramedSpriteSheet + * frames will contain the sprite sheet frames + * + */ + this.frames = []; +}; + +// constructor +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; + +/** + * Loads image or takes it from cache + * + * @method load + */ +PIXI.ImageLoader.prototype.load = function() +{ + if(!this.texture.baseTexture.hasLoaded) + { + var scope = this; + this.texture.baseTexture.addEventListener("loaded", function() + { + scope.onLoaded(); + }); + } + else + { + this.onLoaded(); + } +}; + +/** + * Invoked when image file is loaded or it is already cached and ready to use + * + * @method onLoaded + * @private + */ +PIXI.ImageLoader.prototype.onLoaded = function() +{ + this.dispatchEvent({type: "loaded", content: this}); +}; + +/** + * Loads image and split it to uniform sized frames + * + * + * @method loadFramedSpriteSheet + * @param frameWidth {Number} with of each frame + * @param frameHeight {Number} height of each frame + * @param textureName {String} if given, the frames will be cached in - format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y x1 && x < x1 + this.width) + if(x >= x1 && x <= x1 + this.width) { var y1 = this.y; - - if(y > y1 && y < y1 + this.height) + + if(y >= y1 && y <= y1 + this.height) { return true; } @@ -165,7 +165,7 @@ PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; * @class Polygon * @constructor * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arugments passed can be + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. @@ -232,9 +232,9 @@ PIXI.Polygon.prototype.contains = function(x, y) return inside; } +// constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; - /** * @author Chad Engler */ @@ -256,7 +256,7 @@ PIXI.Circle = function(x, y, radius) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number @@ -306,6 +306,7 @@ PIXI.Circle.prototype.contains = function(x, y) return (dx + dy <= r2); } +// constructor PIXI.Circle.prototype.constructor = PIXI.Circle; @@ -320,8 +321,8 @@ PIXI.Circle.prototype.constructor = PIXI.Circle; * @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 width {Number} The overall height of this ellipse - * @param height {Number} The overall width of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { @@ -331,21 +332,21 @@ PIXI.Ellipse = function(x, y, width, height) * @default 0 */ this.x = x || 0; - + /** * @property y * @type Number * @default 0 */ this.y = y || 0; - + /** * @property width * @type Number * @default 0 */ this.width = width || 0; - + /** * @property height * @type Number @@ -394,11 +395,11 @@ PIXI.Ellipse.getBounds = function() return new PIXI.Rectangle(this.x, this.y, this.width, this.height); } +// constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! @@ -426,7 +427,7 @@ PIXI.mat3.create = function() matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -442,7 +443,7 @@ PIXI.mat3.identity = function(matrix) matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; - + return matrix; } @@ -469,35 +470,35 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat3.multiply = function (mat, mat2, dest) +PIXI.mat3.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], 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; } @@ -514,11 +515,11 @@ PIXI.mat3.clone = function(mat) matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; - + return matrix; } -PIXI.mat3.transpose = function (mat, dest) +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) { @@ -546,30 +547,30 @@ PIXI.mat3.transpose = function (mat, dest) return dest; } -PIXI.mat3.toMat4 = function (mat, 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; } @@ -597,19 +598,19 @@ PIXI.mat4.create = function() matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; - + return matrix; } -PIXI.mat4.transpose = function (mat, dest) +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 (!dest || mat === dest) { 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]; @@ -624,7 +625,7 @@ PIXI.mat4.transpose = function (mat, dest) mat[14] = a23; return mat; } - + dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; @@ -644,18 +645,18 @@ PIXI.mat4.transpose = function (mat, dest) return dest; } -PIXI.mat4.multiply = function (mat, mat2, dest) +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]; + 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; @@ -705,7 +706,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -779,15 +779,6 @@ PIXI.DisplayObject = function() */ this.renderable = false; - /** - * [read-only] The visibility of the object based on world (parent) factors. - * - * @property worldVisible - * @type Boolean - * @readOnly - */ - this.worldVisible = false; - /** * [read-only] The display object container that contains this display object. * @@ -806,15 +797,6 @@ PIXI.DisplayObject = function() */ this.stage = null; - /** - * [read-only] The index of this object in the parent's `children` array - * - * @property childIndex - * @type Number - * @readOnly - */ - this.childIndex = 0; - /** * [read-only] The multiplied alpha of the displayobject * @@ -953,17 +935,6 @@ PIXI.DisplayObject = function() // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObject.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - } -});*/ - /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false @@ -1011,16 +982,57 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { }, set: function(value) { - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -1031,19 +1043,21 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; // 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.mask = mask; - end.mask = mask; + start.data = data; + end.data = data; start.first = start.last = this; end.first = end.last = this; @@ -1051,9 +1065,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) start.open = true; /* - * * insert start - * */ var childFirst = start @@ -1084,9 +1096,7 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) // now insert the end filter block.. /* - * * insert end filter - * */ var childFirst = end var childLast = end @@ -1125,8 +1135,6 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) this.__renderGroup.addFilterBlocks(start, end); } - mask.renderable = false; - } /* @@ -1135,13 +1143,14 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; + var startBlock = data.start; + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; @@ -1151,9 +1160,8 @@ PIXI.DisplayObject.prototype.removeFilter = function() this.first = startBlock._iNext; - // remove the end filter - var lastBlock = this.last; + var lastBlock = data.end; var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; @@ -1162,8 +1170,6 @@ PIXI.DisplayObject.prototype.removeFilter = function() previousObject._iNext = nextObject; // this is always true too! -// if(this.last == lastBlock) - //{ var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; @@ -1174,15 +1180,11 @@ PIXI.DisplayObject.prototype.removeFilter = function() if(!updateLast)break; } - var mask = startBlock.mask - mask.renderable = true; - // if webGL... if(this.__renderGroup) { this.__renderGroup.removeFilterBlocks(startBlock, lastBlock); } - //} } /* @@ -1194,7 +1196,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty - if(this.rotation != this.rotationCache) + if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); @@ -1236,9 +1238,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1270,18 +1275,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -1299,11 +1292,10 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } child.parent = this; - child.childIndex = this.children.length; this.children.push(child); - // updae the stage refference.. + // update the stage refference.. if(this.stage) { @@ -1326,7 +1318,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) var previousObject; // this could be wrong if there is a filter?? - if(this.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -1338,7 +1330,6 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) nextObject = previousObject._iNext; // always true in this case - //this.last = child.last; // need to make sure the parents last is updated too var updateLast = this; var prevLast = previousObject; @@ -1402,7 +1393,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject; var previousObject; @@ -1410,7 +1401,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) if(index == this.children.length) { previousObject = this.last; - var updateLast = this;//.parent; + var updateLast = this; var prevLast = this.last; while(updateLast) { @@ -1539,7 +1530,7 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { // unlink // // modify the list.. - var childFirst = child.first + var childFirst = child.first; var childLast = child.last; var nextObject = childLast._iNext; @@ -1609,7 +1600,6 @@ PIXI.DisplayObjectContainer.prototype.updateTransform = function() this.children[i].updateTransform(); } } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1739,9 +1729,18 @@ PIXI.Sprite.prototype.setTexture = function(texture) if(this.texture.baseTexture != texture.baseTexture) { this.textureChange = true; + this.texture = texture; + + if(this.__renderGroup) + { + this.__renderGroup.updateTexture(this); + } + } + else + { + this.texture = texture; } - this.texture = texture; this.updateFrame = true; } @@ -1798,7 +1797,6 @@ PIXI.Sprite.fromImage = function(imageId) return new PIXI.Sprite(texture); } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1814,7 +1812,7 @@ PIXI.Sprite.fromImage = function(imageId) PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); - + /** * The array of textures that make up the animation * @@ -1822,7 +1820,7 @@ PIXI.MovieClip = function(textures) * @type Array */ this.textures = textures; - + /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * @@ -1848,7 +1846,7 @@ PIXI.MovieClip = function(textures) * @type Function */ this.onComplete = null; - + /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * @@ -1857,8 +1855,8 @@ PIXI.MovieClip = function(textures) * @default 0 * @readOnly */ - this.currentFrame = 0; - + this.currentFrame = 0; + /** * [read-only] Indicates if the MovieClip is currently playing * @@ -1873,6 +1871,23 @@ PIXI.MovieClip = function(textures) PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + /** * Stops the MovieClip * @@ -1928,11 +1943,13 @@ PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) PIXI.MovieClip.prototype.updateTransform = function() { PIXI.Sprite.prototype.updateTransform.call(this); - + if(!this.playing)return; - + this.currentFrame += this.animationSpeed; + var round = (this.currentFrame + 0.5) | 0; + if(this.loop || round < this.textures.length) { this.setTexture(this.textures[round % this.textures.length]); @@ -1952,14 +1969,49 @@ PIXI.MovieClip.prototype.updateTransform = function() -PIXI.FilterBlock = function(mask) +PIXI.FilterBlock = function() { - this.graphics = mask this.visible = true; this.renderable = true; } +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.ColorMatrixFilter = function() +{ + // set the uniforms + this.uniforms = { + matrix: {type: 'mat4', value: [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]}, + }; + + this.fragmentSrc = [ + "precision mediump float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform float invert;", + "uniform mat4 matrix;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * matrix;", + "gl_FragColor = gl_FragColor * vColor;", + "}" + ]; + +} +Object.defineProperty(PIXI.ColorMatrixFilter.prototype, 'matrix', { + get: function() { + return this.uniforms.matrix.value; + }, + set: function(value) { + this.uniforms.matrix.value = value; + } +}); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -1988,7 +2040,7 @@ PIXI.Text = function(text, style) this.setText(text); this.setStyle(style); - + this.updateText(); this.dirty = false; }; @@ -2030,7 +2082,7 @@ PIXI.Text.prototype.setStyle = function(style) * @methos setText * @param {String} text The copy that you would like the text to display */ -PIXI.Sprite.prototype.setText = function(text) +PIXI.Text.prototype.setText = function(text) { this.text = text.toString() || " "; this.dirty = true; @@ -2045,9 +2097,9 @@ PIXI.Sprite.prototype.setText = function(text) PIXI.Text.prototype.updateText = function() { this.context.font = this.style.font; - + var outputText = this.text; - + // word wrap // preserve original text if(this.style.wordWrap)outputText = this.wordWrap(this.text); @@ -2065,7 +2117,7 @@ PIXI.Text.prototype.updateText = function() maxLineWidth = Math.max(maxLineWidth, lineWidth); } this.canvas.width = maxLineWidth + this.style.strokeThickness; - + //calculate text height var lineHeight = this.determineFontHeight("font: " + this.style.font + ";") + this.style.strokeThickness; this.canvas.height = lineHeight * lines.length; @@ -2073,7 +2125,7 @@ PIXI.Text.prototype.updateText = function() //set canvas text styles this.context.fillStyle = this.style.fill; this.context.font = this.style.font; - + this.context.strokeStyle = this.style.stroke; this.context.lineWidth = this.style.strokeThickness; @@ -2083,7 +2135,7 @@ PIXI.Text.prototype.updateText = function() for (i = 0; i < lines.length; i++) { var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); - + if(this.style.align == "right") { linePosition.x += maxLineWidth - lineWidths[i]; @@ -2103,7 +2155,7 @@ PIXI.Text.prototype.updateText = function() this.context.fillText(lines[i], linePosition.x, linePosition.y); } } - + this.updateTexture(); }; @@ -2119,10 +2171,10 @@ PIXI.Text.prototype.updateTexture = function() this.texture.baseTexture.height = this.canvas.height; this.texture.frame.width = this.canvas.width; this.texture.frame.height = this.canvas.height; - + this._width = this.canvas.width; this._height = this.canvas.height; - + PIXI.texturesToUpdate.push(this.texture.baseTexture); }; @@ -2136,10 +2188,10 @@ PIXI.Text.prototype.updateTransform = function() { if(this.dirty) { - this.updateText(); + this.updateText(); this.dirty = false; } - + PIXI.Sprite.prototype.updateTransform.call(this); }; @@ -2151,12 +2203,12 @@ PIXI.Text.prototype.updateTransform = function() * @param fontStyle {Object} * @private */ -PIXI.Text.prototype.determineFontHeight = function(fontStyle) +PIXI.Text.prototype.determineFontHeight = function(fontStyle) { // build a little reference dictionary so if the font style has been used return a // cached version... var result = PIXI.Text.heightCache[fontStyle]; - + if(!result) { var body = document.getElementsByTagName("body")[0]; @@ -2165,13 +2217,13 @@ PIXI.Text.prototype.determineFontHeight = function(fontStyle) dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle + ';position:absolute;top:0;left:0'); body.appendChild(dummy); - + result = dummy.offsetHeight; PIXI.Text.heightCache[fontStyle] = result; - + body.removeChild(dummy); } - + return result; }; @@ -2191,7 +2243,7 @@ PIXI.Text.prototype.wordWrap = function(text) if(p == start) { return 1; } - + if(ctx.measureText(text.substring(0,p)).width <= wrapWidth) { if(ctx.measureText(text.substring(0,p+1)).width > wrapWidth) @@ -2208,7 +2260,7 @@ PIXI.Text.prototype.wordWrap = function(text) return arguments.callee(ctx, text, start, p, wrapWidth); } }; - + var lineWrap = function(ctx, text, wrapWidth) { if(ctx.measureText(text).width <= wrapWidth || text.length < 1) @@ -2218,14 +2270,14 @@ PIXI.Text.prototype.wordWrap = function(text) var pos = searchWrapPos(ctx, text, 0, text.length, wrapWidth); return text.substring(0, pos) + "\n" + arguments.callee(ctx, text.substring(pos), wrapWidth); }; - + var result = ""; var lines = text.split("\n"); for (var i = 0; i < lines.length; i++) { result += lineWrap(this.context, lines[i], this.style.wordWrapWidth) + "\n"; } - + return result; }; @@ -2241,7 +2293,7 @@ PIXI.Text.prototype.destroy = function(destroyTexture) { this.texture.destroy(); } - + }; PIXI.Text.heightCache = {}; @@ -2252,7 +2304,7 @@ PIXI.Text.heightCache = {}; /** * A Text Object will create a line(s) of text using bitmap font. To split a line you can use "\n", "\r" or "\r\n" - * You can generate the fnt files using + * You can generate the fnt files using * http://www.angelcode.com/products/bmfont/ for windows or * http://www.bmglyph.com/ for mac. * @@ -2342,7 +2394,7 @@ PIXI.BitmapText.prototype.updateText = function() prevCharCode = null; continue; } - + var charData = data.chars[charCode]; if(!charData) continue; @@ -2405,7 +2457,7 @@ PIXI.BitmapText.prototype.updateTransform = function() this.dirty = false; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); }; @@ -2414,10 +2466,8 @@ PIXI.BitmapText.fonts = {}; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ - - - -/** + + /** * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * This manager also supports multitouch. * @@ -2451,6 +2501,8 @@ PIXI.InteractionManager = function(stage) */ this.touchs = {}; + + // helpers this.tempPoint = new PIXI.Point(); //this.tempMatrix = mat3.create(); @@ -2461,7 +2513,19 @@ PIXI.InteractionManager = function(stage) this.pool = []; this.interactiveItems = []; + this.interactionDOMElement = null; + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + + this.last = 0; } @@ -2486,7 +2550,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj { var child = children[i]; - if(child.visible) { +// if(child.visible) { // push all interactive bits if(child.interactive) { @@ -2508,7 +2572,7 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj this.collectInteractiveSprite(child, iParent); } } - } +// } } } @@ -2521,27 +2585,68 @@ PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObj */ PIXI.InteractionManager.prototype.setTarget = function(target) { + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + 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 + * to receive those events + * + * @method setTargetDomElement + * @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); + } + + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. - target.view.style["-ms-content-zooming"] = "none"; - target.view.style["-ms-touch-action"] = "none" + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; // DO some window specific touch! } - - this.target = target; - target.view.addEventListener('mousemove', this.onMouseMove.bind(this), true); - target.view.addEventListener('mousedown', this.onMouseDown.bind(this), true); - document.body.addEventListener('mouseup', this.onMouseUp.bind(this), true); - target.view.addEventListener('mouseout', this.onMouseUp.bind(this), true); - - // aint no multi touch just yet! - target.view.addEventListener("touchstart", this.onTouchStart.bind(this), true); - target.view.addEventListener("touchend", this.onTouchEnd.bind(this), true); - target.view.addEventListener("touchmove", this.onTouchMove.bind(this), true); + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); } + /** * updates the state of interactive objects * @@ -2583,12 +2688,14 @@ PIXI.InteractionManager.prototype.update = function() // loop through interactive objects! var length = this.interactiveItems.length; - this.target.view.style.cursor = "default"; + this.interactionDOMElement.style.cursor = "default"; for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - if(!item.visible)continue; + + + //if(!item.visible)continue; // OPTIMISATION - only calculate every time if the mousemove function exists.. // OK so.. does the object have any other interactive functions? @@ -2604,7 +2711,7 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode)this.target.view.style.cursor = "pointer"; + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; if(!item.__isOver) { @@ -2639,7 +2746,7 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); @@ -2669,7 +2776,6 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) */ PIXI.InteractionManager.prototype.onMouseDown = function(event) { - event.preventDefault(); this.mouse.originalEvent = event || window.event; //IE uses window.event // loop through inteaction tree... @@ -2706,6 +2812,26 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) } } + +PIXI.InteractionManager.prototype.onMouseOut = function(event) +{ + var length = this.interactiveItems.length; + + this.interactionDOMElement.style.cursor = "default"; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +} + /** * Is called when the mouse button is released on the renderer element * @@ -2770,7 +2896,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - if(!item.visible)return false; + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), worldTransform = item.worldTransform, @@ -2840,14 +2966,14 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) */ PIXI.InteractionManager.prototype.onTouchMove = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; // update the touch position touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); @@ -2871,10 +2997,7 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) */ PIXI.InteractionManager.prototype.onTouchStart = function(event) { - event.preventDefault(); - this.mouse.originalEvent = event || window.event; //IE uses window.event - - var rect = this.target.view.getBoundingClientRect(); + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2884,6 +3007,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); + touchData.originalEvent = event || window.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); @@ -2921,8 +3046,8 @@ PIXI.InteractionManager.prototype.onTouchStart = function(event) */ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { - this.mouse.originalEvent = event || window.event; //IE uses window.event - var rect = this.target.view.getBoundingClientRect(); + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; for (var i=0; i < changedTouches.length; i++) @@ -2943,7 +3068,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(itemTouchData == touchData) { // so this one WAS down... - + touchData.originalEvent = event || window.event; // hitTest?? if(item.touchend || item.tap) @@ -3055,9 +3180,8 @@ PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; * @constructor * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format * like: 0xFFFFFF for white - * @param interactive {Boolean} enable / disable interaction (default is false) */ -PIXI.Stage = function(backgroundColor, interactive) +PIXI.Stage = function(backgroundColor) { PIXI.DisplayObjectContainer.call( this ); @@ -3077,7 +3201,7 @@ PIXI.Stage = function(backgroundColor, interactive) * @property interactive * @type Boolean */ - this.interactive = interactive; + this.interactive = true; /** * The interaction manage for this stage, manages all interactive activity on the stage @@ -3113,6 +3237,18 @@ PIXI.Stage = function(backgroundColor, interactive) PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +} + /* * Updates the object transform for rendering * @@ -3122,6 +3258,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage; PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; + this.vcount = PIXI.visibleCount; for(var i=0,j=this.children.length; i 100) { console.log("BREAK") break } - } + } } @@ -3361,14 +3466,14 @@ PIXI.runList = function(item) PIXI.EventTarget = function () { var listeners = {}; - + this.addEventListener = this.on = function ( type, listener ) { - - + + if ( listeners[ type ] === undefined ) { listeners[ type ] = []; - + } if ( listeners[ type ].indexOf( listener ) === - 1 ) { @@ -3379,11 +3484,17 @@ PIXI.EventTarget = function () { }; this.dispatchEvent = this.emit = function ( event ) { - - for ( var listener in listeners[ event.type ] ) { - listeners[ event.type ][ listener ]( event ); - + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + } }; @@ -3417,8 +3528,11 @@ PIXI.EventTarget = function () { * @param height {Number} the height of the renderers 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 antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias */ -PIXI.autoDetectRenderer = function(width, height, view, transparent) +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) { if(!width)width = 800; if(!height)height = 600; @@ -3429,7 +3543,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) //console.log(webgl); if( webgl ) { - return new PIXI.WebGLRenderer(width, height, view, transparent); + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); } return new PIXI.CanvasRenderer(width, height, view, transparent); @@ -3441,7 +3555,7 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) PolyK library url: http://polyk.ivank.net Released under MIT licence. - + Copyright (c) 2012 Ivan Kuckir Permission is hereby granted, free of charge, to any person @@ -3465,8 +3579,8 @@ PIXI.autoDetectRenderer = function(width, height, view, transparent) FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - This is an amazing lib! - + This is an amazing lib! + slightly modified by mat groves (matgroves.com); */ @@ -3482,13 +3596,13 @@ PIXI.PolyK = {}; PIXI.PolyK.Triangulate = function(p) { var sign = true; - + var n = p.length>>1; if(n<3) return []; var tgs = []; var avl = []; for(var i=0; i 3) @@ -3496,11 +3610,11 @@ PIXI.PolyK.Triangulate = function(p) var i0 = avl[(i+0)%al]; var i1 = avl[(i+1)%al]; var i2 = avl[(i+2)%al]; - + var ax = p[2*i0], ay = p[2*i0+1]; var bx = p[2*i1], by = p[2*i1+1]; var cx = p[2*i2], cy = p[2*i2+1]; - + var earFound = false; if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) { @@ -3519,7 +3633,7 @@ PIXI.PolyK.Triangulate = function(p) al--; i = 0; } - else if(i++ > 3*al) + else if(i++ > 3*al) { // need to flip flip reverse it! // reset! @@ -3528,17 +3642,17 @@ PIXI.PolyK.Triangulate = function(p) var tgs = []; avl = []; for(var i=0; i= 0) == sign; } - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -3612,13 +3725,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -3658,7 +3769,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -3685,6 +3795,8 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; +PIXI.shaderStack = []; + PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; @@ -3699,27 +3811,26 @@ PIXI.initPrimitiveShader = function() shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } PIXI.initDefaultStripShader = function() @@ -3736,9 +3847,7 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); PIXI.stripShaderProgram = shaderProgram; @@ -3789,35 +3898,135 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) return shaderProgram; } +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); -PIXI.activateDefaultShader = function() + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; +} + + +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; gl.useProgram(shaderProgram); - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - gl.useProgram(PIXI.primitiveProgram); - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); } +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + /** * @author Mat Groves http://matgroves.com/ @Doormat23 @@ -3887,7 +4096,7 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. @@ -3899,8 +4108,10 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -4243,7 +4454,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) verts.push(px , py); verts.push(r, g, b, alpha); - verts.push(p2x - (px-p2x), p2y - (py - p2y));//, 4); + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } @@ -4356,9 +4567,10 @@ PIXI.gl; * @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 antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) * */ -PIXI.WebGLRenderer = function(width, height, view, transparent) +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { // do a catch.. only 1 webGL renderer.. @@ -4382,7 +4594,7 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) { PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, - antialias:true, // SPEED UP?? + antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); @@ -4392,11 +4604,12 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -4413,7 +4626,10 @@ PIXI.WebGLRenderer = function(width, height, view, transparent) this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -4469,8 +4685,6 @@ PIXI.WebGLRenderer.prototype.render = function(stage) { // TODO make this work // dont think this is needed any more? - //if(this.__stage)this.checkVisibility(this.__stage, false) - this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } @@ -4487,10 +4701,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) // update any textures PIXI.WebGLRenderer.updateTextures(); - // recursivly loop through all items! - //this.checkVisibility(stage, true); - // update the scene graph + PIXI.visibleCount++; stage.updateTransform(); var gl = this.gl; @@ -4499,16 +4711,12 @@ PIXI.WebGLRenderer.prototype.render = function(stage) gl.colorMask(true, true, true, this.transparent); gl.viewport(0, 0, this.width, this.height); - // set the correct matrix.. - // gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.projectionMatrix); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - //PIXI.projectionMatrix = this.projectionMatrix; this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); @@ -4546,8 +4754,9 @@ PIXI.WebGLRenderer.prototype.render = function(stage) */ PIXI.WebGLRenderer.updateTextures = function() { - for (var i=0; i < PIXI.texturesToUpdate.length; i++) this.updateTexture(PIXI.texturesToUpdate[i]); - for (var i=0; i < PIXI.texturesToDestroy.length; i++) this.destroyTexture(PIXI.texturesToDestroy[i]); + //TODO break this out into a texture manager... + for (var i=0; i < PIXI.texturesToUpdate.length; i++) PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + for (var i=0; i < PIXI.texturesToDestroy.length; i++) PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; } @@ -4562,6 +4771,7 @@ PIXI.WebGLRenderer.updateTextures = function() */ PIXI.WebGLRenderer.updateTexture = function(texture) { + //TODO break this out into a texture manager... var gl = PIXI.gl; if(!texture._glTexture) @@ -4602,9 +4812,10 @@ PIXI.WebGLRenderer.updateTexture = function(texture) * @param texture {Texture} The texture to update * @private */ -PIXI.WebGLRenderer.prototype.destroyTexture = function(texture) +PIXI.WebGLRenderer.destroyTexture = function(texture) { - var gl = this.gl; + //TODO break this out into a texture manager... + var gl = PIXI.gl; if(texture._glTexture) { @@ -4768,7 +4979,6 @@ PIXI.WebGLBatch.prototype.clean = function() this.uvs = []; this.indices = []; this.colors = []; - //this.sprites = []; this.dynamicSize = 1; this.texture = null; this.last = null; @@ -4805,7 +5015,6 @@ PIXI.WebGLBatch.prototype.init = function(sprite) this.dirty = true; this.blendMode = sprite.blendMode; this.texture = sprite.texture.baseTexture; -// this.sprites.push(sprite); this.head = sprite; this.tail = sprite; this.size = 1; @@ -4838,7 +5047,6 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) else { this.head = sprite; - //this.head.__prev = null } } @@ -4926,7 +5134,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) { this.dirty = true; - var batch = new PIXI.WebGLBatch(this.gl);//PIXI._getBatch(this.gl); + var batch = new PIXI.WebGLBatch(this.gl); batch.init(sprite); batch.texture = this.texture; batch.tail = this.tail; @@ -4936,8 +5144,6 @@ PIXI.WebGLBatch.prototype.split = function(sprite) sprite.__prev = null; // return a splite batch! - //sprite.__prev.__next = null; - //sprite.__prev = null; // TODO this size is wrong! // need to recalculate :/ problem with a linked list! @@ -5007,13 +5213,13 @@ PIXI.WebGLBatch.prototype.growBatch = function() gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW); - this.uvs = new Float32Array( this.dynamicSize * 8 ) + 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 ) + this.colors = new Float32Array( this.dynamicSize * 4 ); gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW); @@ -5112,7 +5318,7 @@ PIXI.WebGLBatch.prototype.update = function() while(displayObject) { - if(displayObject.worldVisible) + if(displayObject.vcount === PIXI.visibleCount) { width = displayObject.texture.frame.width; height = displayObject.texture.frame.height; @@ -5214,7 +5420,7 @@ PIXI.WebGLBatch.prototype.update = function() PIXI.WebGLBatch.prototype.render = function(start, end) { start = start || 0; - //end = end || this.size; + if(end == undefined)end = this.size; if(this.dirty) @@ -5230,8 +5436,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -5239,6 +5446,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -5262,13 +5471,11 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - //var startIndex = 0//1; var len = end - start; - // console.log(this.size) + // DRAW THAT this! gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 ); } @@ -5336,77 +5543,45 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - - // TODO remove this by replacing visible with getter setters.. - this.checkVisibility(this.root, this.root.visible); - // 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; } - else if(renderable instanceof PIXI.TilingSprite) + + // non sprite batch.. + var worldVisible = renderable.vcount === PIXI.visibleCount; + + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);//, projectionMatrix); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, false); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } } -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - -} - /** * Renders a specific displayObject * @@ -5420,11 +5595,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project PIXI.WebGLRenderer.updateTextures(); var gl = this.gl; - this.checkVisibility(displayObject, displayObject.visible); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); -// gl.uniformMatrix4fv(PIXI.shaderProgram.mvMatrixUniform, false, projectionMatrix); - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - // to do! // render part of the scene... @@ -5483,7 +5655,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } if(lastRenderable instanceof PIXI.Sprite) @@ -5577,45 +5749,80 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + + var worldVisible = renderable.vcount === PIXI.visibleCount + if(renderable instanceof PIXI.TilingSprite) { - if(renderable.visible)this.renderTilingSprite(renderable, projection); + if(worldVisible)this.renderTilingSprite(renderable, projection); } else if(renderable instanceof PIXI.Strip) { - if(renderable.visible)this.renderStrip(renderable, projection); + if(worldVisible)this.renderStrip(renderable, projection); } else if(renderable instanceof PIXI.CustomRenderable) { - if(renderable.visible) renderable.renderWebGL(this, projection); + if(worldVisible) renderable.renderWebGL(this, projection); } else if(renderable instanceof PIXI.Graphics) { - if(renderable.visible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); + if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection); } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -5623,42 +5830,6 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } } -/** - * Checks the visibility of a displayObject - * - * @method checkVisibility - * @param displayObject {DisplayObject} - * @param globalVisible {Boolean} - * @private - */ -PIXI.WebGLRenderGroup.prototype.checkVisibility = function(displayObject, globalVisible) -{ - // give the dp a reference to its renderGroup... - var children = displayObject.children; - //displayObject.worldVisible = globalVisible; - for (var i=0; i < children.length; i++) - { - var child = children[i]; - - // TODO optimize... should'nt need to loop through everything all the time - child.worldVisible = child.visible && globalVisible; - - // everything should have a batch! - // time to see whats new! - if(child.textureChange) - { - child.textureChange = false; - if(child.worldVisible)this.updateTexture(child); - // update texture!! - } - - if(child.children.length > 0) - { - this.checkVisibility(child, child.worldVisible); - } - }; -} - /** * Updates a webgl texture * @@ -5719,7 +5890,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + while(previousRenderable != this.root.first) { previousRenderable = previousRenderable._iPrev; if(previousRenderable.renderable && previousRenderable.__renderGroup)break; @@ -5733,7 +5904,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -6105,6 +6276,7 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) } } + /** * Initializes a tiling sprite * @@ -6175,23 +6347,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - + var shaderProgram = PIXI.stripShaderProgram; - gl.useProgram(PIXI.stripShaderProgram); + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); PIXI.mat3.transpose(m); // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -6249,11 +6417,10 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } - //console.log(gl.TRIANGLE_STRIP); gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - gl.useProgram(PIXI.shaderProgram); + gl.useProgram(PIXI.currentProgram); } /** @@ -6328,6 +6495,7 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW); } + /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -6401,7 +6569,6 @@ PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; */ PIXI.CanvasRenderer.prototype.render = function(stage) { - // update children if need be //stage.__childrenAdded = []; //stage.__childrenRemoved = []; @@ -6410,6 +6577,7 @@ PIXI.CanvasRenderer.prototype.render = function(stage) PIXI.texturesToUpdate = []; PIXI.texturesToDestroy = []; + PIXI.visibleCount++; stage.updateTransform(); // update the background color @@ -6496,7 +6664,7 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) var frame = displayObject.texture.frame; - if(frame) + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; @@ -6534,31 +6702,34 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - // context.fillStyle = 0xFF0000; - // context.fillRect(0, 0, 200, 200); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; - //context.globalCompositeOperation = 'lighter'; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - //context.globalCompositeOperation = 'source-over'; - context.restore(); + // only masks supported right now! } } // count++ @@ -6651,7 +6822,7 @@ PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite) PIXI.CanvasRenderer.prototype.renderStrip = function(strip) { var context = this.context; - //context.globalCompositeOperation = 'lighter'; + // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; @@ -6678,8 +6849,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x2, y2); context.closePath(); - // context.fillStyle = "white"//rgb(1, 1, 1,1)); - // context.fill(); context.clip(); @@ -6703,7 +6872,6 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.restore(); }; -// context.globalCompositeOperation = 'source-over'; } /** @@ -6718,7 +6886,7 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) */ PIXI.CanvasGraphics = function() { - + } @@ -6734,35 +6902,33 @@ PIXI.CanvasGraphics = function() PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - - for (var i=0; i < graphics.graphicsData.length; i++) + + for (var i=0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); context.lineWidth = data.lineWidth; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); - + context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6777,21 +6943,20 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.RECT) { - - // TODO - need to be Undefined! - if(data.fillColor) + + if(data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); context.fillRect(points[0], points[1], points[2], points[3]); - + } if(data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeRect(points[0], points[1], points[2], points[3]); } - + } else if(data.type == PIXI.Graphics.CIRC) { @@ -6799,7 +6964,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.beginPath(); context.arc(points[0], points[1], points[2],0,2*Math.PI); context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6814,19 +6979,19 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - + var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6834,15 +6999,15 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - + context.closePath(); - + if(data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; @@ -6855,7 +7020,7 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) context.stroke(); } } - + }; } @@ -6871,37 +7036,35 @@ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var worldAlpha = graphics.worldAlpha; - + var len = graphics.graphicsData.length; if(len > 1) { len = 1; console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object") } - - for (var i=0; i < 1; i++) + + for (var i=0; i < 1; i++) { var data = graphics.graphicsData[i]; var points = data.points; - + if(data.type == PIXI.Graphics.POLY) { - //if(data.lineWidth <= 0)continue; - context.beginPath(); context.moveTo(points[0], points[1]); - + for (var j=1; j < points.length/2; j++) { context.lineTo(points[j * 2], points[j * 2 + 1]); - } - + } + // if the first and last point are the same close the path - much neater :) if(points[0] == points[points.length-2] && points[1] == points[points.length-1]) { context.closePath(); } - + } else if(data.type == PIXI.Graphics.RECT) { @@ -6918,18 +7081,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) } else if(data.type == PIXI.Graphics.ELIP) { - + // elipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas var elipseData = data.points; - + var w = elipseData[2] * 2; var h = elipseData[3] * 2; - + var x = elipseData[0] - w/2; var y = elipseData[1] - h/2; - + context.beginPath(); - + var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical @@ -6937,7 +7100,7 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle - + context.moveTo(x, ym); context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); @@ -6945,8 +7108,8 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); context.closePath(); } - - + + }; } @@ -6956,18 +7119,18 @@ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) /** - * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * 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 * - * @class Graphics + * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call( this ); - + this.renderable = true; /** @@ -7028,14 +7191,14 @@ PIXI.Graphics.prototype.constructor = PIXI.Graphics; PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - + this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha == undefined) ? 1 : alpha; - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.graphicsData.push(this.currentPath); } @@ -7049,12 +7212,12 @@ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) PIXI.Graphics.prototype.moveTo = function(x, y) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; - + this.currentPath.points.push(x, y); - + this.graphicsData.push(this.currentPath); } @@ -7084,7 +7247,7 @@ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; - this.fillAlpha = alpha || 1; + this.fillAlpha = (alpha == undefined) ? 1 : alpha; } /** @@ -7110,11 +7273,11 @@ PIXI.Graphics.prototype.endFill = function() PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.RECT}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7130,11 +7293,11 @@ PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) PIXI.Graphics.prototype.drawCircle = function( x, y, radius) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7151,11 +7314,11 @@ PIXI.Graphics.prototype.drawCircle = function( x, y, radius) PIXI.Graphics.prototype.drawElipse = function( x, y, width, height) { if(this.currentPath.points.length == 0)this.graphicsData.pop(); - - this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, - fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[x, y, width, height], type:PIXI.Graphics.ELIP}; - + this.graphicsData.push(this.currentPath); this.dirty = true; } @@ -7169,7 +7332,7 @@ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; - + this.dirty = true; this.clearDirty = true; this.graphicsData = []; @@ -7190,20 +7353,20 @@ PIXI.Strip = function(texture, width, height) PIXI.DisplayObjectContainer.call( this ); this.texture = texture; this.blendMode = PIXI.blendModes.NORMAL; - + try { this.uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0,1]); - + this.verticies = new Float32Array([0, 0, 0,0, 0,0, 0, 0, 0]); - + this.colors = new Float32Array([1, 1, 1, 1]); - + this.indices = new Uint16Array([0, 1, 2, 3]); } catch(error) @@ -7211,18 +7374,18 @@ PIXI.Strip = function(texture, width, height) this.uvs = [0, 1, 1, 1, 1, 0, 0,1]; - + this.verticies = [0, 0, 0,0, 0,0, 0, 0, 0]; - + this.colors = [1, 1, 1, 1]; - + this.indices = [0, 1, 2, 3]; } - - + + /* this.uvs = new Float32Array() this.verticies = new Float32Array() @@ -7231,7 +7394,7 @@ PIXI.Strip = function(texture, width, height) */ this.width = width; this.height = height; - + // load the texture! if(texture.baseTexture.hasLoaded) { @@ -7244,7 +7407,7 @@ PIXI.Strip = function(texture, width, height) this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } - + this.renderable = true; } @@ -7256,8 +7419,8 @@ PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; @@ -7280,7 +7443,7 @@ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; - + try { this.verticies = new Float32Array( points.length * 4); @@ -7291,12 +7454,12 @@ PIXI.Rope = function(texture, points) catch(error) { this.verticies = verticies - + this.uvs = uvs this.colors = colors this.indices = indices } - + this.refresh(); } @@ -7309,99 +7472,99 @@ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1)return; - + var uvs = this.uvs var indices = this.indices; var colors = this.colors; - + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - + + uvs[0] = 0 uvs[1] = 1 uvs[2] = 0 uvs[3] = 1 - + colors[0] = 1; colors[1] = 1; - + indices[0] = 0; indices[1] = 1; - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; // time to do some smart drawing! var amount = i/(total-1) - + if(i%2) { uvs[index] = amount; uvs[index+1] = 0; - + uvs[index+2] = amount uvs[index+3] = 1 - + } else { uvs[index] = amount uvs[index+1] = 0 - + uvs[index+2] = amount uvs[index+3] = 1 } - + index = i * 2; colors[index] = 1; colors[index+1] = 1; - + index = i * 2; indices[index] = index; indices[index + 1] = index + 1; - + lastPoint = point; } } PIXI.Rope.prototype.updateTransform = function() { - + var points = this.points; if(points.length < 1)return; - - var verticies = this.verticies - + + var verticies = this.verticies + var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; var point = points[0]; - + this.count-=0.2; - - verticies[0] = point.x + perp.x + + verticies[0] = point.x + perp.x verticies[1] = point.y + perp.y //+ 200 - verticies[2] = point.x - perp.x + verticies[2] = point.x - perp.x verticies[3] = point.y - perp.y//+200 // time to do some smart drawing! - + var total = points.length; - - for (var i = 1; i < total; i++) + + for (var i = 1; i < total; i++) { - + var point = points[i]; var index = i * 4; - + if(i < points.length-1) { nextPoint = points[i+1]; @@ -7410,35 +7573,35 @@ PIXI.Rope.prototype.updateTransform = function() { nextPoint = point } - + perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; - + var ratio = (1 - (i / (total-1))) * 10; if(ratio > 1)ratio = 1; - + var perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); var num = this.texture.height/2//(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; perp.x /= perpLength; perp.y /= perpLength; - + perp.x *= num; perp.y *= num; - - verticies[index] = point.x + perp.x + + verticies[index] = point.x + perp.x verticies[index+1] = point.y + perp.y - verticies[index+2] = point.x - perp.x + verticies[index+2] = point.x - perp.x verticies[index+3] = point.y - perp.y lastPoint = point; } - + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); } PIXI.Rope.prototype.setTexture = function(texture) { - // stop current texture + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7494,7 +7657,7 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tileScale * @type Point - */ + */ this.tileScale = new PIXI.Point(1,1); /** @@ -7502,11 +7665,11 @@ PIXI.TilingSprite = function(texture, width, height) * * @property tilePosition * @type Point - */ + */ this.tilePosition = new PIXI.Point(0,0); this.renderable = true; - + this.blendMode = PIXI.blendModes.NORMAL } @@ -7524,8 +7687,8 @@ PIXI.TilingSprite.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY - - // stop current texture + + // stop current texture this.texture = texture; this.updateFrame = true; } @@ -7546,10 +7709,10 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) /** * @author Mat Groves http://matgroves.com/ @Doormat23 * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi - * + * * Awesome JS run time provided by EsotericSoftware * https://github.com/EsotericSoftware/spine-runtimes - * + * */ /** @@ -7562,48 +7725,41 @@ PIXI.TilingSprite.prototype.onTextureUpdate = function(event) * @constructor * @param url {String} The url of the spine anim file to be used */ -PIXI.Spine = function(url) -{ +PIXI.Spine = function (url) { PIXI.DisplayObjectContainer.call(this); - + this.spineData = PIXI.AnimCache[url]; - - if(!this.spineData) - { + + if (!this.spineData) { throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); - return; } - - this.count = 0; - - this.sprites = []; - + this.skeleton = new spine.Skeleton(this.spineData); this.skeleton.updateWorldTransform(); - this.stateData = new spine.AnimationStateData(this.spineData); + this.stateData = new spine.AnimationStateData(this.spineData); this.state = new spine.AnimationState(this.stateData); - - // add the sprites.. - for (var i = 0; i < this.skeleton.drawOrder.length; i++) { - - var attachmentName = this.skeleton.drawOrder[i].data.attachmentName; - - // kind of an assumtion here. that its a png - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; - } - - - var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(attachmentName)); - sprite.anchor.x = sprite.anchor.y = 0.5; - this.addChild(sprite); - this.sprites.push(sprite); - }; -} -PIXI.Spine.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Spine.prototype.constructor = PIXI.Spine; /* @@ -7612,55 +7768,74 @@ PIXI.Spine.prototype.constructor = PIXI.Spine; * @method updateTransform * @private */ -PIXI.Spine.prototype.updateTransform = function() -{ - // TODO should make this time based really.. - this.state.update(1/60); +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); this.state.apply(this.skeleton); this.skeleton.updateWorldTransform(); - - for (var i = 0; i < this.skeleton.drawOrder.length; i++) - { - var slot = this.skeleton.drawOrder[i]; + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } - var x = slot.bone.worldX + slot.attachment.x * slot.bone.m00 + slot.attachment.y * slot.bone.m01 + slot.attachment.width * 0.5; - var y = slot.bone.worldY + slot.attachment.x * slot.bone.m10 + slot.attachment.y * slot.bone.m11 + slot.attachment.height * 0.5; - //console.log(x + ' : ' + y); - - - //console.log(slot.attachment.name) - if(slot.cacheName != slot.attachment.name) - { - var attachmentName = slot.attachment.name; - - if(!PIXI.TextureCache[attachmentName]) - { - attachmentName += ".png"; + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; } - - this.sprites[i].setTexture(PIXI.TextureCache[attachmentName]); - - slot.cacheName = slot.attachment.name; + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; } - - x += -((slot.attachment.width * (slot.bone.worldScaleX + slot.attachment.scaleX - 1))>>1); - y += -((slot.attachment.height * (slot.bone.worldScaleY + slot.attachment.scaleY - 1))>>1); - - - this.sprites[i].position.x = x; - this.sprites[i].position.y = y; - this.sprites[i].rotation = (-(slot.bone.worldRotation + slot.attachment.rotation)) * (Math.PI/180); - } - + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -} +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; /* * Awesome JS run time provided by EsotericSoftware - * + * * https://github.com/EsotericSoftware/spine-runtimes - * + * */ var spine = {}; @@ -7770,7 +7945,7 @@ spine.Slot.prototype = { this.g = data.g; this.b = data.b; this.a = data.a; - + var slotDatas = this.skeleton.data.slots; for (var i = 0, n = slotDatas.length; i < n; i++) { if (slotDatas[i] == data) { @@ -8001,6 +8176,7 @@ spine.TranslateTimeline.prototype = { var frameTime = frames[frameIndex]; var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; } @@ -8025,14 +8201,12 @@ spine.ScaleTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - + var bone = skeleton.bones[this.boneIndex]; if (time >= frames[frames.length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; - - return; } @@ -8070,6 +8244,7 @@ spine.ColorTimeline.prototype = { apply: function (skeleton, time, alpha) { var frames = this.frames; if (time < frames[0]) return; // Time is before first frame. + var slot = skeleton.slots[this.slotIndex]; if (time >= frames[frames.length - 5]) { // Time is after last frame. @@ -8119,7 +8294,7 @@ spine.AttachmentTimeline = function (frameCount) { spine.AttachmentTimeline.prototype = { slotIndex: 0, getFrameCount: function () { - return this.frames.length / 2; + return this.frames.length; }, setFrame: function (frameIndex, time, attachmentName) { this.frames[frameIndex] = time; @@ -8136,11 +8311,6 @@ spine.AttachmentTimeline.prototype = { frameIndex = spine.binarySearch(frames, time, 1) - 1; var attachmentName = this.attachmentNames[frameIndex]; - //console.log(skeleton.slots[this.slotIndex]) - - // change the name! - // skeleton.slots[this.slotIndex].attachmentName = attachmentName; - skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); } }; @@ -8312,11 +8482,9 @@ spine.Skeleton.prototype = { if (slot.data.name == slotName) { var attachment = null; if (attachmentName) { - attachment = this.getAttachment(i, attachmentName); if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; } - slot.setAttachment(attachment); return; } @@ -8398,7 +8566,6 @@ spine.RegionAttachment.prototype = { offset[7/*Y4*/] = localYCos + localX2Sin; }, computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; y += bone.worldY; var m00 = bone.m00; @@ -8422,6 +8589,7 @@ spine.AnimationStateData = function (skeletonData) { this.animationToMixTime = {}; }; spine.AnimationStateData.prototype = { + defaultMix: 0, setMixByName: function (fromName, toName, duration) { var from = this.skeletonData.findAnimation(fromName); if (!from) throw "Animation not found: " + fromName; @@ -8434,7 +8602,7 @@ spine.AnimationStateData.prototype = { }, getMix: function (from, to) { var time = this.animationToMixTime[from.name + ":" + to.name]; - return time ? time : 0; + return time ? time : this.defaultMix; } }; @@ -8474,7 +8642,7 @@ spine.AnimationState.prototype = { this.previous = null; } this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); - } else + } else this.current.apply(skeleton, this.currentTime, this.currentLoop); }, clearAnimation: function () { @@ -8620,16 +8788,9 @@ spine.SkeletonJson.prototype = { name = map["name"] || name; var type = spine.AttachmentType[map["type"] || "region"]; - - // @ekelokorpi - // var attachment = this.attachmentLoader.newAttachment(skin, type, name); - var attachment = new spine.RegionAttachment(); - - // @Doormat23 - // add the name of the attachment - attachment.name = name; - + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); attachment.x = (map["x"] || 0) * this.scale; attachment.y = (map["y"] || 0) * this.scale; attachment.scaleX = map["scaleX"] || 1; @@ -8638,10 +8799,19 @@ spine.SkeletonJson.prototype = { attachment.width = (map["width"] || 32) * this.scale; attachment.height = (map["height"] || 32) * this.scale; attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; } - return attachment; + throw "Unknown attachment type: " + type; }, + readAnimation: function (name, map, skeletonData) { var timelines = []; var duration = 0; @@ -8692,7 +8862,7 @@ spine.SkeletonJson.prototype = { } timelines.push(timeline); duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - + } else throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; } @@ -8735,8 +8905,8 @@ spine.SkeletonJson.prototype = { timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); } timelines.push(timeline); - // PIXI FIX - duration = Math.max(duration, timeline.frames[Math.floor(timeline.getFrameCount()) - 1]); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } else throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; } @@ -9005,14 +9175,14 @@ spine.Bone.yDown = true; /** * This object is one that will allow you to specify custom rendering functions based on render type * - * @class CustomRenderable + * @class CustomRenderable * @extends DisplayObject * @constructor */ PIXI.CustomRenderable = function() { PIXI.DisplayObject.call( this ); - + } // constructor @@ -9118,19 +9288,19 @@ PIXI.BaseTexture = function(source) 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 } ); @@ -9143,7 +9313,7 @@ PIXI.BaseTexture = function(source) this.hasLoaded = true; this.width = this.source.width; this.height = this.source.height; - + PIXI.texturesToUpdate.push(this); } @@ -9183,7 +9353,7 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); + var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; @@ -9211,7 +9381,7 @@ PIXI.FrameCache = {}; * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frmae {Rectangle} The rectangle frame of the texture to show + * @param frame {Rectangle} The rectangle frame of the texture to show */ PIXI.Texture = function(baseTexture, frame) { @@ -9256,7 +9426,7 @@ PIXI.Texture = function(baseTexture, frame) { if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); //console.log(frame) - + this.setFrame(frame); } else @@ -9335,13 +9505,13 @@ PIXI.Texture.prototype.setFrame = function(frame) PIXI.Texture.fromImage = function(imageUrl, crossorigin) { var texture = PIXI.TextureCache[imageUrl]; - + if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin)); PIXI.TextureCache[imageUrl] = texture; } - + return texture; } @@ -9391,7 +9561,7 @@ PIXI.Texture.addTextureToCache = function(texture, id) } /** - * Remove a texture from the textureCache. + * Remove a texture from the textureCache. * * @static * @method removeTextureFromCache @@ -9503,15 +9673,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // create a projection matrix.. this.projection = new PIXI.Point(this.width/2 , this.height/2); -/* - this.projectionMatrix = PIXI.mat4.create(); - this.projectionMatrix[5] = 2/this.height// * 0.5; - this.projectionMatrix[13] = -1; - - this.projectionMatrix[0] = 2/this.width; - this.projectionMatrix[12] = -1; -*/ // set the correct render function.. this.render = this.renderWebGL; @@ -9525,10 +9687,6 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - //this.frame.width = this.width - //this.frame.height = this.height; - - if(PIXI.gl) { this.projection.x = this.width/2 @@ -9592,6 +9750,7 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var children = displayObject.children; //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; @@ -9604,8 +9763,9 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle displayObject.worldTransform[5] -= position.y; } - - + PIXI.visibleCount++; + displayObject.vcount = PIXI.visibleCount; + for(var i=0,j=children.length; i- format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y= 0; i--) { var child = children[i]; - + // if(child.visible) { // push all interactive bits if(child.interactive) @@ -146,7 +144,7 @@ PIXI.InteractionManager.prototype.setTarget = function(target) PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) { //remove previouse listeners - if( this.interactionDOMElement !== null ) + if( this.interactionDOMElement !== null ) { this.interactionDOMElement.style['-ms-content-zooming'] = ''; this.interactionDOMElement.style['-ms-touch-action'] = ''; @@ -162,12 +160,12 @@ PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) } - if (window.navigator.msPointerEnabled) + if (window.navigator.msPointerEnabled) { // time to remove some of that zoom in ja.. domElement.style['-ms-content-zooming'] = 'none'; domElement.style['-ms-touch-action'] = 'none'; - + // DO some window specific touch! } @@ -193,7 +191,7 @@ PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) PIXI.InteractionManager.prototype.update = function() { if(!this.target)return; - + // frequency of 30fps?? var now = Date.now(); var diff = now - this.last; @@ -201,44 +199,44 @@ PIXI.InteractionManager.prototype.update = function() if(diff < 1)return; this.last = now; // - + // ok.. so mouse events?? // yes for now :) // OPTIMSE - how often to check?? if(this.dirty) { this.dirty = false; - + var len = this.interactiveItems.length; - + for (var i=0; i < len; i++) { this.interactiveItems[i].interactiveChildren = false; } - + this.interactiveItems = []; - + if(this.stage.interactive)this.interactiveItems.push(this.stage); // go through and collect all the objects that are interactive.. this.collectInteractiveSprite(this.stage, this.stage); } - + // loop through interactive objects! var length = this.interactiveItems.length; - - this.interactionDOMElement.style.cursor = "default"; - + + this.interactionDOMElement.style.cursor = "default"; + for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - - + + //if(!item.visible)continue; - + // 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) { // ok so there are some functions so lets hit test it.. @@ -248,13 +246,13 @@ PIXI.InteractionManager.prototype.update = function() // loks like there was a hit! if(item.__hit) { - if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; - + if(item.buttonMode) this.interactionDOMElement.style.cursor = "pointer"; + if(!item.__isOver) { - + if(item.mouseover)item.mouseover(this.mouse); - item.__isOver = true; + item.__isOver = true; } } else @@ -263,11 +261,11 @@ PIXI.InteractionManager.prototype.update = function() { // roll out! if(item.mouseout)item.mouseout(this.mouse); - item.__isOver = false; + item.__isOver = false; } } } - + // ---> } } @@ -284,18 +282,18 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) this.mouse.originalEvent = event || window.event; //IE uses window.event // TODO optimize by not check EVERY TIME! maybe half as often? // var rect = this.interactionDOMElement.getBoundingClientRect(); - + this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); - + var length = this.interactiveItems.length; var global = this.mouse.global; - - + + for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - + if(item.mousemove) { //call the function! @@ -314,34 +312,34 @@ PIXI.InteractionManager.prototype.onMouseMove = function(event) PIXI.InteractionManager.prototype.onMouseDown = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event - + // loop through inteaction tree... - // hit test each item! -> + // hit test each item! -> // get interactive items under point?? //stage.__i var length = this.interactiveItems.length; var global = this.mouse.global; - + var index = 0; var parent = this.stage; - - // while - // hit test + + // while + // hit test for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - + if(item.mousedown || item.click) { item.__mouseIsDown = true; item.__hit = this.hitTest(item, this.mouse); - + if(item.__hit) { //call the function! if(item.mousedown)item.mousedown(this.mouse); item.__isDown = true; - + // just the one! if(!item.interactiveChildren)break; } @@ -353,18 +351,18 @@ PIXI.InteractionManager.prototype.onMouseDown = function(event) PIXI.InteractionManager.prototype.onMouseOut = function(event) { var length = this.interactiveItems.length; - - this.interactionDOMElement.style.cursor = "default"; - + + this.interactionDOMElement.style.cursor = "default"; + for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - + if(item.__isOver) { this.mouse.target = item; if(item.mouseout)item.mouseout(this.mouse); - item.__isOver = false; + item.__isOver = false; } } } @@ -379,21 +377,21 @@ PIXI.InteractionManager.prototype.onMouseOut = function(event) PIXI.InteractionManager.prototype.onMouseUp = function(event) { this.mouse.originalEvent = event || window.event; //IE uses window.event - + var global = this.mouse.global; - - + + var length = this.interactiveItems.length; var up = false; - + for (var i = 0; i < length; i++) { var item = this.interactiveItems[i]; - + if(item.mouseup || item.mouseupoutside || item.click) { item.__hit = this.hitTest(item, this.mouse); - + if(item.__hit && !up) { //call the function! @@ -405,7 +403,7 @@ PIXI.InteractionManager.prototype.onMouseUp = function(event) { if(item.click)item.click(this.mouse); } - + if(!item.interactiveChildren)up = true; } else @@ -415,8 +413,8 @@ PIXI.InteractionManager.prototype.onMouseUp = function(event) if(item.mouseupoutside)item.mouseupoutside(this.mouse); } } - - item.__isDown = false; + + item.__isDown = false; } } } @@ -432,7 +430,7 @@ PIXI.InteractionManager.prototype.onMouseUp = function(event) PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) { var global = interactionData.global; - + if(item.vcount !== PIXI.visibleCount)return false; var isSprite = (item instanceof PIXI.Sprite), @@ -444,7 +442,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) y = a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id; interactionData.target = item; - + //a sprite or display object with a hit area defined if(item.hitArea && item.hitArea.contains) { if(item.hitArea.contains(x, y)) { @@ -453,7 +451,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) return true; } - + return false; } // a sprite with no hitarea defined @@ -463,11 +461,11 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) height = item.texture.frame.height, x1 = -width * item.anchor.x, y1; - + if(x > x1 && x < x1 + width) { y1 = -height * item.anchor.y; - + if(y > y1 && y < y1 + height) { // set the target property if a hit is true! @@ -478,7 +476,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) } var length = item.children.length; - + for (var i = 0; i < length; i++) { var tempItem = item.children[i]; @@ -491,7 +489,7 @@ PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) } } - return false; + return false; } /** @@ -505,18 +503,18 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) { var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) + + for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; touchData.originalEvent = event || window.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); } - + var length = this.interactiveItems.length; for (var i = 0; i < length; i++) { @@ -535,38 +533,38 @@ PIXI.InteractionManager.prototype.onTouchMove = function(event) PIXI.InteractionManager.prototype.onTouchStart = function(event) { var rect = this.interactionDOMElement.getBoundingClientRect(); - + var changedTouches = event.changedTouches; - for (var i=0; i < changedTouches.length; i++) + for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; - + var touchData = this.pool.pop(); if(!touchData)touchData = new PIXI.InteractionData(); - + touchData.originalEvent = event || window.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); - + var length = this.interactiveItems.length; - + for (var j = 0; j < length; j++) { var item = this.interactiveItems[j]; - + if(item.touchstart || item.tap) { item.__hit = this.hitTest(item, touchData); - + if(item.__hit) { //call the function! if(item.touchstart)item.touchstart(touchData); item.__isDown = true; item.__touchData = touchData; - + if(!item.interactiveChildren)break; } } @@ -586,28 +584,28 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) //this.mouse.originalEvent = event || window.event; //IE uses window.event var rect = this.interactionDOMElement.getBoundingClientRect(); var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) + + for (var i=0; i < changedTouches.length; i++) { var touchEvent = changedTouches[i]; var touchData = this.touchs[touchEvent.identifier]; 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); - + var length = this.interactiveItems.length; for (var j = 0; j < length; j++) { var item = this.interactiveItems[j]; var itemTouchData = item.__touchData; // <-- Here! item.__hit = this.hitTest(item, touchData); - + if(itemTouchData == touchData) { // so this one WAS down... touchData.originalEvent = event || window.event; // hitTest?? - + if(item.touchend || item.tap) { if(item.__hit && !up) @@ -617,7 +615,7 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) { if(item.tap)item.tap(touchData); } - + if(!item.interactiveChildren)up = true; } else @@ -627,16 +625,16 @@ PIXI.InteractionManager.prototype.onTouchEnd = function(event) if(item.touchendoutside)item.touchendoutside(touchData); } } - + item.__isDown = false; } - + item.__touchData = null; - + } else { - + } } // remove the touch.. @@ -656,11 +654,11 @@ PIXI.InteractionData = function() /** * This point stores the global coords of where the touch/mouse event happened * - * @property global + * @property global * @type Point */ this.global = new PIXI.Point(); - + // this is here for legacy... but will remove this.local = new PIXI.Point(); @@ -692,7 +690,7 @@ 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], diff --git a/src/pixi/display/DisplayObject.js b/src/pixi/display/DisplayObject.js index f7874f3..455284d 100644 --- a/src/pixi/display/DisplayObject.js +++ b/src/pixi/display/DisplayObject.js @@ -12,7 +12,6 @@ PIXI.DisplayObject = function() { this.last = this; this.first = this; - /** * The coordinate of the object relative to the local coordinates of the parent. * @@ -50,7 +49,7 @@ PIXI.DisplayObject = function() * * @property alpha * @type Number - */ + */ this.alpha = 1; /** @@ -58,7 +57,7 @@ PIXI.DisplayObject = function() * * @property visible * @type Boolean - */ + */ this.visible = true; /** @@ -67,7 +66,7 @@ PIXI.DisplayObject = function() * * @property hitArea * @type Rectangle|Circle|Ellipse|Polygon - */ + */ this.hitArea = null; /** @@ -92,7 +91,7 @@ PIXI.DisplayObject = function() * @property parent * @type DisplayObjectContainer * @readOnly - */ + */ this.parent = null; /** @@ -101,7 +100,7 @@ PIXI.DisplayObject = function() * @property stage * @type Stage * @readOnly - */ + */ this.stage = null; /** @@ -268,7 +267,7 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', { }, set: function(value) { this._interactive = value; - + // TODO more to be done here.. // need to sort out a re-crawl! if(this.stage)this.stage.dirty = true; @@ -288,17 +287,58 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { return this._mask; }, set: function(value) { - - this._mask = 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; + } + + this._mask = value; + } +}); + +/** + * Sets the filters for the displayObject. Currently there's a few limitations. + * 1: At the moment only one filter can be applied at a time.. + * 2: They cannot be nested. + * 3: There's no padding yet. + * 4: this is a webGL only feature. + * @property filters + * @type Array + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + //if(value == ) + if(value) + { + if(this._filters)this.removeFilter(this._filters); this.addFilter(value) } else { - this.removeFilter(); + if(this._filters)this.removeFilter(this._filters); } + + this._filters = value; } }); @@ -309,54 +349,58 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { * @param mask {Graphics} the graphics object to use as a filter * @private */ -PIXI.DisplayObject.prototype.addFilter = function(mask) +PIXI.DisplayObject.prototype.addFilter = function(data) { - if(this.filter)return; - this.filter = true; - + //if(this.filter)return; + //this.filter = true; + // insert a filter block.. + // TODO Onject pool thease bad boys.. var start = new PIXI.FilterBlock(); var end = new PIXI.FilterBlock(); - - start.mask = mask; - end.mask = mask; - + + 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; - + /* * 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; + previousObject._iNext = childFirst; } else { nextObject = this; - } - + } + if(nextObject) { nextObject._iPrev = childLast; childLast._iNext = nextObject; } - - + + // now insert the end filter block.. - + /* * insert end filter */ @@ -364,21 +408,21 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) var childLast = end var nextObject = null; var previousObject = null; - + previousObject = this.last; nextObject = previousObject._iNext; - + if(nextObject) { nextObject._iPrev = childLast; childLast._iNext = nextObject; } - + childFirst._iPrev = previousObject; - previousObject._iNext = childFirst; - + previousObject._iNext = childFirst; + var updateLast = this; - + var prevLast = this.last; while(updateLast) { @@ -388,17 +432,15 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) } updateLast = updateLast.parent; } - + this.first = start; - + // if webGL... if(this.__renderGroup) { this.__renderGroup.addFilterBlocks(start, end); } - - mask.renderable = false; - + } /* @@ -407,34 +449,34 @@ PIXI.DisplayObject.prototype.addFilter = function(mask) * @method removeFilter * @private */ -PIXI.DisplayObject.prototype.removeFilter = function() +PIXI.DisplayObject.prototype.removeFilter = function(data) { - if(!this.filter)return; - this.filter = false; - + //if(!this.filter)return; + //this.filter = false; + console.log("YUOIO") // modify the list.. - var startBlock = this.first; - + var startBlock = data.start; + + var nextObject = startBlock._iNext; var previousObject = startBlock._iPrev; - + if(nextObject)nextObject._iPrev = previousObject; - if(previousObject)previousObject._iNext = nextObject; - + if(previousObject)previousObject._iNext = nextObject; + this.first = startBlock._iNext; - - + // remove the end filter - var lastBlock = this.last; - + var lastBlock = data.end; + var nextObject = lastBlock._iNext; var previousObject = lastBlock._iPrev; - + if(nextObject)nextObject._iPrev = previousObject; - previousObject._iNext = nextObject; - + previousObject._iNext = nextObject; + // this is always true too! - var tempLast = lastBlock._iPrev; + var tempLast = lastBlock._iPrev; // need to make sure the parents last is updated too var updateLast = this; while(updateLast.last == lastBlock) @@ -443,10 +485,7 @@ PIXI.DisplayObject.prototype.removeFilter = function() updateLast = updateLast.parent; if(!updateLast)break; } - - var mask = startBlock.mask - mask.renderable = true; - + // if webGL... if(this.__renderGroup) { @@ -468,8 +507,8 @@ PIXI.DisplayObject.prototype.updateTransform = function() 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; @@ -478,12 +517,12 @@ PIXI.DisplayObject.prototype.updateTransform = function() 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], @@ -493,7 +532,7 @@ PIXI.DisplayObject.prototype.updateTransform = function() localTransform[2] = a02 localTransform[5] = a12 - + worldTransform[0] = b00 * a00 + b01 * a10; worldTransform[1] = b00 * a01 + b01 * a11; worldTransform[2] = b00 * a02 + b01 * a12 + b02; @@ -505,7 +544,7 @@ PIXI.DisplayObject.prototype.updateTransform = function() // 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; } diff --git a/src/pixi/display/DisplayObjectContainer.js b/src/pixi/display/DisplayObjectContainer.js index a438b3f..68ba15c 100644 --- a/src/pixi/display/DisplayObjectContainer.js +++ b/src/pixi/display/DisplayObjectContainer.js @@ -7,21 +7,21 @@ * A DisplayObjectContainer represents a collection of display objects. * It is the base class of all display objects that act as a container for other objects. * - * @class DisplayObjectContainer + * @class DisplayObjectContainer * @extends DisplayObject * @constructor */ PIXI.DisplayObjectContainer = function() { PIXI.DisplayObject.call( this ); - + /** * [read-only] The of children of this container. * * @property children * @type Array * @readOnly - */ + */ this.children = []; } @@ -29,18 +29,6 @@ PIXI.DisplayObjectContainer = function() PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; -//TODO make visible a getter setter -/* -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'visible', { - get: function() { - return this._visible; - }, - set: function(value) { - this._visible = value; - - } -});*/ - /** * Adds a child to the container. * @@ -51,18 +39,18 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) { if(child.parent != undefined) { - + //// COULD BE THIS??? child.parent.removeChild(child); // return; } child.parent = this; - - this.children.push(child); - + + this.children.push(child); + // update the stage refference.. - + if(this.stage) { var tmpChild = child; @@ -71,20 +59,20 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) 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.filter) + if(this._filters) { previousObject = this.last._iPrev; } @@ -94,12 +82,12 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } 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) @@ -108,15 +96,15 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) } updateLast = updateLast.parent; } - + if(nextObject) { nextObject._iPrev = childLast; childLast._iNext = nextObject; } - + childFirst._iPrev = previousObject; - previousObject._iNext = childFirst; + previousObject._iNext = childFirst; // need to remove any render groups.. if(this.__renderGroup) @@ -126,7 +114,7 @@ PIXI.DisplayObjectContainer.prototype.addChild = function(child) // add them to the new render group.. this.__renderGroup.addDisplayObjectAndChildren(child); } - + } /** @@ -145,7 +133,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) child.parent.removeChild(child); } child.parent = this; - + if(this.stage) { var tmpChild = child; @@ -157,13 +145,13 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) } 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; @@ -186,18 +174,18 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) { 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; + previousObject._iNext = childFirst; this.children.splice(index, 0, child); // need to remove any render groups.. @@ -208,7 +196,7 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) // add them to the new render group.. this.__renderGroup.addDisplayObjectAndChildren(child); } - + } else { @@ -227,21 +215,21 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) { /* - * this funtion needs to be recoded.. + * this funtion needs to be recoded.. * can be done a lot faster.. */ return; - + // need to fix this function :/ /* // TODO I already know this?? var index = this.children.indexOf( child ); var index2 = this.children.indexOf( child2 ); - - if ( index !== -1 && index2 !== -1 ) + + if ( index !== -1 && index2 !== -1 ) { // cool - + /* if(this.stage) { @@ -249,15 +237,15 @@ PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) // TODO sure there is a nicer way to achieve this! this.stage.__removeChild(child); this.stage.__removeChild(child2); - + this.stage.__addChild(child); this.stage.__addChild(child2); } - + // swap the positions.. this.children[index] = child2; this.children[index2] = child; - + } else { @@ -292,22 +280,22 @@ PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { var index = this.children.indexOf( child ); - if ( index !== -1 ) + 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; - + previousObject._iNext = nextObject; + if(this.last == childLast) { - var tempLast = childFirst._iPrev; + var tempLast = childFirst._iPrev; // need to make sure the parents last is updated too var updateLast = this; while(updateLast.last == childLast.last) @@ -317,10 +305,10 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) if(!updateLast)break; } } - + childLast._iNext = null; childFirst._iPrev = null; - + // update the stage reference.. if(this.stage) { @@ -330,16 +318,16 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) if(tmpChild.interactive)this.stage.dirty = true; tmpChild.stage = null; tmpChild = tmpChild._iNext; - } + } while(tmpChild) } - + // webGL trim if(child.__renderGroup) { child.__renderGroup.removeDisplayObjectAndChildren(child); } - + child.parent = undefined; this.children.splice( index, 1 ); } @@ -358,11 +346,11 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) PIXI.DisplayObjectContainer.prototype.updateTransform = function() { if(!this.visible)return; - + PIXI.DisplayObject.prototype.updateTransform.call( this ); - + for(var i=0,j=this.children.length; i 0) { PIXI.Texture.frameUpdates = []; } - - + + } /** @@ -121,7 +121,7 @@ PIXI.CanvasRenderer.prototype.resize = function(width, height) { this.width = width; this.height = height; - + this.view.width = width; this.view.height = height; } @@ -138,50 +138,50 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) // no loger recurrsive! var transform; var context = this.context; - + context.globalCompositeOperation = 'source-over'; - - // one the display object hits this. we can break the loop + + // one the display object hits this. we can break the loop var testObject = displayObject.last._iNext; displayObject = displayObject.first; - - do + + 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; - + if(frame && frame.width && frame.height) { context.globalAlpha = displayObject.worldAlpha; - + context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); - - context.drawImage(displayObject.texture.baseTexture.source, + + context.drawImage(displayObject.texture.baseTexture.source, frame.x, frame.y, frame.width, frame.height, - (displayObject.anchor.x) * -frame.width, + (displayObject.anchor.x) * -frame.width, (displayObject.anchor.y) * -frame.height, frame.width, frame.height); - } + } } else if(displayObject instanceof PIXI.Strip) { @@ -204,37 +204,44 @@ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject) } else if(displayObject instanceof PIXI.FilterBlock) { - if(displayObject.open) + if(PIXI.FilterBlock.data instanceof PIXI.Graphics) { - context.save(); - - var cacheAlpha = displayObject.mask.alpha; - var maskTransform = displayObject.mask.worldTransform; - - context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) - - displayObject.mask.worldAlpha = 0.5; - - context.worldAlpha = 0; - - PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); - context.clip(); - - displayObject.mask.worldAlpha = cacheAlpha; + if(displayObject.open) + { + context.save(); + + var cacheAlpha = displayObject.mask.alpha; + var maskTransform = displayObject.mask.worldTransform; + + context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]) + + displayObject.mask.worldAlpha = 0.5; + + context.worldAlpha = 0; + + PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, context); + context.clip(); + + displayObject.mask.worldAlpha = cacheAlpha; + } + else + { + context.restore(); + } } else { - context.restore(); + // only masks supported right now! } } // count++ displayObject = displayObject._iNext; - - + + } while(displayObject != testObject) - + } /** @@ -249,26 +256,26 @@ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) var context = this.context; var verticies = strip.verticies; var uvs = strip.uvs; - + var length = verticies.length/2; this.count++; - + context.beginPath(); - for (var i=1; i < length-2; i++) + for (var i=1; i < length-2; i++) { - + // draw some triangles! var index = i*2; - + var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4]; var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5]; - + context.moveTo(x0, y0); context.lineTo(x1, y1); context.lineTo(x2, y2); - - }; - + + }; + context.fillStyle = "#FF0000"; context.fill(); context.closePath(); @@ -284,26 +291,26 @@ PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) 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(); } @@ -321,18 +328,18 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) // draw triangles!! var verticies = strip.verticies; var uvs = strip.uvs; - + var length = verticies.length/2; this.count++; - for (var i=1; i < length-2; i++) + for (var i=1; i < length-2; i++) { - + // draw some triangles! var index = i*2; - + var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4]; var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5]; - + var u0 = uvs[index] * strip.texture.width, u1 = uvs[index+2] * strip.texture.width, u2 = uvs[index+4]* strip.texture.width; var v0 = uvs[index+1]* strip.texture.height, v1 = uvs[index+3] * strip.texture.height, v2 = uvs[index+5]* strip.texture.height; @@ -343,10 +350,10 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) context.lineTo(x1, y1); context.lineTo(x2, y2); context.closePath(); - + context.clip(); - - + + // Compute matrix transform var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2; var delta_a = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2; @@ -355,16 +362,16 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip) var delta_d = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2; var delta_e = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2; var delta_f = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2 - v0*u1*y2 - u0*y1*v2; - - - - + + + + context.transform(delta_a/delta, delta_d/delta, delta_b/delta, delta_e/delta, delta_c/delta, delta_f/delta); - + context.drawImage(strip.texture.baseTexture.source, 0, 0); context.restore(); }; - + } diff --git a/src/pixi/renderers/webgl/PixiShader.js b/src/pixi/renderers/webgl/PixiShader.js new file mode 100644 index 0000000..97e61a3 --- /dev/null +++ b/src/pixi/renderers/webgl/PixiShader.js @@ -0,0 +1,70 @@ +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PixiShader = function() +{ + // the webGL program.. + this.program; + + this.fragmentSrc = [ + "precision lowp float;", + "varying vec2 vTextureCoord;", + "varying float vColor;", + "uniform sampler2D uSampler;", + "void main(void) {", + "gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;", + "}" + ]; + +} + +PIXI.PixiShader.prototype.init = function() +{ + var program = PIXI.compileProgram(this.vertexSrc || PIXI.shaderVertexSrc, this.fragmentSrc) + + var gl = PIXI.gl; + + gl.useProgram(program); + + // get the default shader bits! + program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition"); + program.colorAttribute = gl.getAttribLocation(program, "aColor"); + program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord"); + + program.projectionVector = gl.getUniformLocation(program, "projectionVector"); + program.samplerUniform = gl.getUniformLocation(program, "uSampler"); + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + program[key] = gl.getUniformLocation(program, key); + } + + this.program = program; +} + +PIXI.PixiShader.prototype.syncUniforms = function() +{ + var gl = PIXI.gl; + + for (var key in this.uniforms) + { + //var + var type = this.uniforms[key].type; + + // need to grow this! + if(type == "f") + { + gl.uniform1f(this.program[key], this.uniforms[key].value); + } + else if(type == "mat4") + { + gl.uniformMatrix4fv(this.program[key], false, this.uniforms[key].value); + } + } + +} + diff --git a/src/pixi/renderers/webgl/WebGLBatch.js b/src/pixi/renderers/webgl/WebGLBatch.js index 8b6305b..7d6758c 100644 --- a/src/pixi/renderers/webgl/WebGLBatch.js +++ b/src/pixi/renderers/webgl/WebGLBatch.js @@ -24,7 +24,7 @@ PIXI._getBatch = function(gl) */ PIXI._returnBatch = function(batch) { - batch.clean(); + batch.clean(); PIXI._batchs.push(batch); } @@ -33,7 +33,7 @@ PIXI._returnBatch = function(batch) */ PIXI._restoreBatchs = function(gl) { - for (var i=0; i < PIXI._batchs.length; i++) + for (var i=0; i < PIXI._batchs.length; i++) { PIXI._batchs[i].restoreLostContext(gl); }; @@ -54,7 +54,7 @@ PIXI._restoreBatchs = function(gl) PIXI.WebGLBatch = function(gl) { this.gl = gl; - + this.size = 0; this.vertexBuffer = gl.createBuffer(); @@ -108,7 +108,7 @@ PIXI.WebGLBatch.prototype.restoreLostContext = function(gl) * @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; @@ -128,7 +128,7 @@ PIXI.WebGLBatch.prototype.init = function(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++; @@ -156,7 +156,7 @@ PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite) * @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++; @@ -184,7 +184,7 @@ PIXI.WebGLBatch.prototype.insertAfter = function(sprite, previousSprite) * * @method remove * @param sprite {Sprite} the sprite to be removed - */ + */ PIXI.WebGLBatch.prototype.remove = function(sprite) { this.size--; @@ -268,7 +268,7 @@ PIXI.WebGLBatch.prototype.split = function(sprite) * Merges two batchs together * * @method merge - * @param batch {WebGLBatch} the batch that will be merged + * @param batch {WebGLBatch} the batch that will be merged */ PIXI.WebGLBatch.prototype.merge = function(batch) { @@ -325,10 +325,10 @@ PIXI.WebGLBatch.prototype.growBatch = function() this.dirtyColors = true; - this.indices = new Uint16Array(this.dynamicSize * 6); + this.indices = new Uint16Array(this.dynamicSize * 6); var length = this.indices.length/6; - for (var i=0; i < length; i++) + for (var i=0; i < length; i++) { var index2 = i * 6; var index3 = i * 4; @@ -381,7 +381,7 @@ PIXI.WebGLBatch.prototype.refresh = function() 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 +5] = (frame.y + frame.height) / th; this.uvs[index +6] = frame.x / tw; this.uvs[index +7] = (frame.y + frame.height) / th; @@ -443,17 +443,17 @@ PIXI.WebGLBatch.prototype.update = function() tx = worldTransform[2]; ty = worldTransform[5]; - this.verticies[index + 0 ] = a * w1 + c * h1 + tx; + this.verticies[index + 0 ] = a * w1 + c * h1 + tx; this.verticies[index + 1 ] = d * h1 + b * w1 + ty; - this.verticies[index + 2 ] = a * w0 + c * h1 + tx; - this.verticies[index + 3 ] = d * h1 + b * w0 + ty; + this.verticies[index + 2 ] = a * w0 + c * h1 + tx; + this.verticies[index + 3 ] = d * h1 + b * w0 + ty; - this.verticies[index + 4 ] = a * w0 + c * h0 + tx; - this.verticies[index + 5 ] = d * h0 + b * w0 + ty; + this.verticies[index + 4 ] = a * w0 + c * h0 + tx; + this.verticies[index + 5 ] = d * h0 + b * w0 + ty; - this.verticies[index + 6] = a * w1 + c * h0 + tx; - this.verticies[index + 7] = d * h0 + b * w1 + ty; + this.verticies[index + 6] = a * w1 + c * h0 + tx; + this.verticies[index + 7] = d * h0 + b * w1 + ty; if(displayObject.updateFrame || displayObject.texture.updateFrame) { @@ -472,7 +472,7 @@ PIXI.WebGLBatch.prototype.update = function() 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 +5] = (frame.y + frame.height) / th; this.uvs[index +6] = frame.x / tw; this.uvs[index +7] = (frame.y + frame.height) / th; @@ -522,7 +522,7 @@ PIXI.WebGLBatch.prototype.render = function(start, end) start = start || 0; if(end == undefined)end = this.size; - + if(this.dirty) { this.refresh(); @@ -536,8 +536,9 @@ PIXI.WebGLBatch.prototype.render = function(start, end) //TODO optimize this! - var shaderProgram = PIXI.shaderProgram; - gl.useProgram(shaderProgram); + var shaderProgram = PIXI.currentShader; + + //gl.useProgram(shaderProgram); // update the verts.. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -545,6 +546,8 @@ PIXI.WebGLBatch.prototype.render = function(start, end) gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // update the uvs + var isDefault = (shaderProgram == PIXI.shaderProgram) + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); if(this.dirtyUVS) @@ -568,7 +571,6 @@ PIXI.WebGLBatch.prototype.render = function(start, end) } gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); diff --git a/src/pixi/renderers/webgl/WebGLGraphics.js b/src/pixi/renderers/webgl/WebGLGraphics.js index 87d98be..55e9661 100644 --- a/src/pixi/renderers/webgl/WebGLGraphics.js +++ b/src/pixi/renderers/webgl/WebGLGraphics.js @@ -9,7 +9,7 @@ */ PIXI.WebGLGraphics = function() { - + } /** @@ -24,62 +24,64 @@ PIXI.WebGLGraphics = function() PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) { var gl = PIXI.gl; - - if(!graphics._webGL)graphics._webGL = {points:[], indices:[], lastIndex:0, + + if(!graphics._webGL)graphics._webGL = {points:[], indices:[], lastIndex:0, buffer:gl.createBuffer(), indexBuffer:gl.createBuffer()}; - + if(graphics.dirty) { graphics.dirty = false; - + if(graphics.clearDirty) { graphics.clearDirty = false; - + graphics._webGL.lastIndex = 0; graphics._webGL.points = []; graphics._webGL.indices = []; - + } - + PIXI.WebGLGraphics.updateGraphics(graphics); } - - + + PIXI.activatePrimitiveShader(); - + // This could be speeded up fo sure! var m = PIXI.mat3.clone(graphics.worldTransform); - + PIXI.mat3.transpose(m); - - // set the matrix transform for the + + // set the matrix transform for the gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - + gl.uniformMatrix3fv(PIXI.primitiveProgram.translationMatrix, false, m); - + gl.uniform2f(PIXI.primitiveProgram.projectionVector, projection.x, projection.y); - + gl.uniform1f(PIXI.primitiveProgram.alpha, graphics.worldAlpha); gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); - + // WHY DOES THIS LINE NEED TO BE THERE??? - gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + //gl.vertexAttribPointer(PIXI.shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); // its not even used.. but need to be set or it breaks? // only on pc though.. - + gl.vertexAttribPointer(PIXI.primitiveProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 4 * 6, 0); gl.vertexAttribPointer(PIXI.primitiveProgram.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); - + // set the index buffer! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.indexBuffer); - + gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); - + + PIXI.deactivatePrimitiveShader(); + // return to default shader... - PIXI.activateDefaultShader(); +// PIXI.activateShader(PIXI.defaultShader); } /** @@ -92,18 +94,18 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection) */ PIXI.WebGLGraphics.updateGraphics = function(graphics) { - for (var i=graphics._webGL.lastIndex; i < graphics.graphicsData.length; i++) + for (var i=graphics._webGL.lastIndex; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; - + if(data.type == PIXI.Graphics.POLY) { if(data.fill) { - if(data.points.length>3) + if(data.points.length>3) PIXI.WebGLGraphics.buildPoly(data, graphics._webGL); } - + if(data.lineWidth > 0) { PIXI.WebGLGraphics.buildLine(data, graphics._webGL); @@ -118,18 +120,18 @@ PIXI.WebGLGraphics.updateGraphics = function(graphics) PIXI.WebGLGraphics.buildCircle(data, graphics._webGL); } }; - + graphics._webGL.lastIndex = graphics.graphicsData.length; - + var gl = PIXI.gl; graphics._webGL.glPoints = new Float32Array(graphics._webGL.points); - + gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer); gl.bufferData(gl.ARRAY_BUFFER, graphics._webGL.glPoints, gl.STATIC_DRAW); - + graphics._webGL.glIndicies = new Uint16Array(graphics._webGL.indices); - + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.glIndicies, gl.STATIC_DRAW); } @@ -147,45 +149,45 @@ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) { // --- // // need to convert points to a nice regular data - // + // var rectData = graphicsData.points; var x = rectData[0]; var y = rectData[1]; var width = rectData[2]; var height = rectData[3]; - - + + if(graphicsData.fill) { var color = HEXtoRGB(graphicsData.fillColor); var alpha = graphicsData.fillAlpha; - + var r = color[0] * alpha; var g = color[1] * alpha; var b = color[2] * alpha; - + var verts = webGLData.points; var indices = webGLData.indices; - + var vertPos = verts.length/6; - + // start verts.push(x, y); verts.push(r, g, b, alpha); - + verts.push(x + width, y); verts.push(r, g, b, alpha); - + verts.push(x , y + height); verts.push(r, g, b, alpha); - + verts.push(x + width, y + height); verts.push(r, g, b, alpha); - + // insert 2 dead triangles.. indices.push(vertPos, vertPos, vertPos+1, vertPos+2, vertPos+3, vertPos+3) } - + if(graphicsData.lineWidth) { graphicsData.points = [x, y, @@ -193,10 +195,10 @@ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) x + width, y + height, x, y + height, x, y]; - + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); } - + } /** @@ -212,16 +214,16 @@ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) { // --- // // need to convert points to a nice regular data - // + // var rectData = graphicsData.points; var x = rectData[0]; var y = rectData[1]; var width = rectData[2]; var height = rectData[3]; - + var totalSegs = 40; var seg = (Math.PI * 2) / totalSegs ; - + if(graphicsData.fill) { var color = HEXtoRGB(graphicsData.fillColor); @@ -230,41 +232,41 @@ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) var r = color[0] * alpha; var g = color[1] * alpha; var b = color[2] * alpha; - + var verts = webGLData.points; var indices = webGLData.indices; - + var vecPos = verts.length/6; - + indices.push(vecPos); - - for (var i=0; i < totalSegs + 1 ; i++) + + for (var i=0; i < totalSegs + 1 ; i++) { verts.push(x,y, r, g, b, alpha); - + verts.push(x + Math.sin(seg * i) * width, y + Math.cos(seg * i) * height, r, g, b, alpha); - + indices.push(vecPos++, vecPos++); }; - + indices.push(vecPos-1); } - + if(graphicsData.lineWidth) { graphicsData.points = []; - - for (var i=0; i < totalSegs + 1; i++) + + for (var i=0; i < totalSegs + 1; i++) { graphicsData.points.push(x + Math.sin(seg * i) * width, y + Math.cos(seg * i) * height) }; - + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); } - + } /** @@ -279,89 +281,89 @@ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) { // TODO OPTIMISE! - + var wrap = true; var points = graphicsData.points; if(points.length == 0)return; - + // get first and last point.. figure out the middle! 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(firstPoint.x == lastPoint.x && firstPoint.y == lastPoint.y) { points.pop(); points.pop(); - + lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); - + var midPointX = lastPoint.x + (firstPoint.x - lastPoint.x) *0.5; var midPointY = lastPoint.y + (firstPoint.y - lastPoint.y) *0.5; - + points.unshift(midPointX, midPointY); points.push(midPointX, midPointY) } - + var verts = webGLData.points; var indices = webGLData.indices; var length = points.length / 2; var indexCount = points.length; var indexStart = verts.length/6; - + // DRAW the Line var width = graphicsData.lineWidth / 2; - + // sort color var color = HEXtoRGB(graphicsData.lineColor); var alpha = graphicsData.lineAlpha; var r = color[0] * alpha; var g = color[1] * alpha; var b = color[2] * alpha; - + var p1x, p1y, p2x, p2y, p3x, p3y; var perpx, perpy, perp2x, perp2y, perp3x, perp3y; var ipx, ipy; var a1, b1, c1, a2, b2, c2; var denom, pdist, dist; - + p1x = points[0]; p1y = points[1]; - + p2x = points[2]; p2y = points[3]; - + perpx = -(p1y - p2y); perpy = p1x - p2x; - + dist = Math.sqrt(perpx*perpx + perpy*perpy); - + perpx /= dist; perpy /= dist; perpx *= width; perpy *= width; - + // start verts.push(p1x - perpx , p1y - perpy, r, g, b, alpha); - + verts.push(p1x + perpx , p1y + perpy, r, g, b, alpha); - - for (var i = 1; i < length-1; i++) + + for (var i = 1; i < length-1; i++) { p1x = points[(i-1)*2]; p1y = points[(i-1)*2 + 1]; - + p2x = points[(i)*2] p2y = points[(i)*2 + 1] - + p3x = points[(i+1)*2]; p3y = points[(i+1)*2 + 1]; - + perpx = -(p1y - p2y); perpy = p1x - p2x; - + dist = Math.sqrt(perpx*perpx + perpy*perpy); perpx /= dist; perpy /= dist; @@ -370,91 +372,91 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) perp2x = -(p2y - p3y); perp2y = p2x - p3x; - + dist = Math.sqrt(perp2x*perp2x + perp2y*perp2y); perp2x /= dist; perp2y /= dist; perp2x *= width; perp2y *= width; - + a1 = (-perpy + p1y) - (-perpy + p2y); b1 = (-perpx + p2x) - (-perpx + p1x); c1 = (-perpx + p1x) * (-perpy + p2y) - (-perpx + p2x) * (-perpy + p1y); a2 = (-perp2y + p3y) - (-perp2y + p2y); b2 = (-perp2x + p2x) - (-perp2x + p3x); c2 = (-perp2x + p3x) * (-perp2y + p2y) - (-perp2x + p2x) * (-perp2y + p3y); - + denom = a1*b2 - a2*b1; - + if (denom == 0) { denom+=1; } - + px = (b1*c2 - b2*c1)/denom; py = (a2*c1 - a1*c2)/denom; - + pdist = (px -p2x) * (px -p2x) + (py -p2y) + (py -p2y); - + if(pdist > 140 * 140) { perp3x = perpx - perp2x; perp3y = perpy - perp2y; - + dist = Math.sqrt(perp3x*perp3x + perp3y*perp3y); perp3x /= dist; perp3y /= dist; perp3x *= width; perp3y *= width; - + verts.push(p2x - perp3x, p2y -perp3y); verts.push(r, g, b, alpha); - + verts.push(p2x + perp3x, p2y +perp3y); verts.push(r, g, b, alpha); - + verts.push(p2x - perp3x, p2y -perp3y); verts.push(r, g, b, alpha); - + indexCount++; } else { verts.push(px , py); verts.push(r, g, b, alpha); - + verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } - + p1x = points[(length-2)*2] - p1y = points[(length-2)*2 + 1] - + p1y = points[(length-2)*2 + 1] + p2x = points[(length-1)*2] p2y = points[(length-1)*2 + 1] - + perpx = -(p1y - p2y) perpy = p1x - p2x; - + dist = Math.sqrt(perpx*perpx + perpy*perpy); perpx /= dist; perpy /= dist; perpx *= width; perpy *= width; - + verts.push(p2x - perpx , p2y - perpy) verts.push(r, g, b, alpha); - + verts.push(p2x + perpx , p2y + perpy) verts.push(r, g, b, alpha); - + indices.push(indexStart); - - for (var i=0; i < indexCount; i++) + + for (var i=0; i < indexCount; i++) { indices.push(indexStart++); }; - + indices.push(indexStart-1); } @@ -471,25 +473,25 @@ PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) { var points = graphicsData.points; if(points.length < 6)return; - + // get first and last point.. figure out the middle! var verts = webGLData.points; var indices = webGLData.indices; - + var length = points.length / 2; - + // sort color var color = HEXtoRGB(graphicsData.fillColor); var alpha = graphicsData.fillAlpha; var r = color[0] * alpha; var g = color[1] * alpha; var b = color[2] * alpha; - + var triangles = PIXI.PolyK.Triangulate(points); - + var vertPos = verts.length / 6; - - for (var i=0; i < triangles.length; i+=3) + + for (var i=0; i < triangles.length; i+=3) { indices.push(triangles[i] + vertPos); indices.push(triangles[i] + vertPos); @@ -497,8 +499,8 @@ PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) indices.push(triangles[i+2] +vertPos); indices.push(triangles[i+2] + vertPos); }; - - for (var i = 0; i < length; i++) + + for (var i = 0; i < length; i++) { verts.push(points[i * 2], points[i * 2 + 1], r, g, b, alpha); diff --git a/src/pixi/renderers/webgl/WebGLRenderGroup.js b/src/pixi/renderers/webgl/WebGLRenderGroup.js index a6507cf..f661a96 100644 --- a/src/pixi/renderers/webgl/WebGLRenderGroup.js +++ b/src/pixi/renderers/webgl/WebGLRenderGroup.js @@ -19,7 +19,7 @@ PIXI.WebGLRenderGroup = function(gl) { this.gl = gl; this.root; - + this.backgroundColor; this.batchs = []; this.toRemove = []; @@ -33,18 +33,18 @@ PIXI.WebGLRenderGroup.prototype.constructor = PIXI.WebGLRenderGroup; * * @method setRenderable * @param displayObject {DisplayObject} - * @private + * @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); @@ -59,26 +59,24 @@ PIXI.WebGLRenderGroup.prototype.setRenderable = function(displayObject) PIXI.WebGLRenderGroup.prototype.render = function(projection) { PIXI.WebGLRenderer.updateTextures(); - + var gl = this.gl; - - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); + + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + 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++) + for (var i=0; i < this.batchs.length; i++) { - + renderable = this.batchs[i]; if(renderable instanceof PIXI.WebGLBatch) { this.batchs[i].render(); continue; } - + // non sprite batch.. var worldVisible = renderable.vcount === PIXI.visibleCount; @@ -96,42 +94,10 @@ PIXI.WebGLRenderGroup.prototype.render = function(projection) } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ - if(renderable.open) - { - gl.enable(gl.STENCIL_TEST); - - gl.colorMask(false, false, false, false); - gl.stencilFunc(gl.ALWAYS,1,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL,0,0xff); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - } - else - { - gl.disable(gl.STENCIL_TEST); - } + this.handleFilterBlock(renderable, projection); } } - -} - -/** - * Renders the stage to its webgl view - * - * @method handleFilter - * @param filter {FilterBlock} - * @private - */ -PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) -{ - + } /** @@ -145,20 +111,19 @@ PIXI.WebGLRenderGroup.prototype.handleFilter = function(filter, projection) PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projection) { PIXI.WebGLRenderer.updateTextures(); - + var gl = this.gl; - - gl.uniform2f(PIXI.shaderProgram.projectionVector, projection.x, projection.y); - + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + // 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 @@ -172,14 +137,14 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project if(nextRenderable.renderable && nextRenderable.__renderGroup)break; } var startBatch = nextRenderable.batch; - + 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) { @@ -188,7 +153,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project else { startIndex = 1; - + while(head.__next != nextRenderable) { startIndex++; @@ -200,7 +165,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project { startBatch = nextRenderable; } - + // Get the LAST renderable object var lastRenderable = displayObject; var endBatch; @@ -208,15 +173,15 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project while(lastItem.children.length > 0) { lastItem = lastItem.children[lastItem.children.length-1]; - if(lastItem.renderable)lastRenderable = lastItem; + if(lastItem.renderable)lastRenderable = lastItem.last; } - + if(lastRenderable instanceof PIXI.Sprite) { endBatch = lastRenderable.batch; - + var head = endBatch.head; - + if(head == lastRenderable) { endIndex = 0; @@ -224,7 +189,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project else { endIndex = 1; - + while(head.__next != lastRenderable) { endIndex++; @@ -236,9 +201,9 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project { endBatch = lastRenderable; } - + // TODO - need to fold this up a bit! - + if(startBatch == endBatch) { if(startBatch instanceof PIXI.WebGLBatch) @@ -251,11 +216,11 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project } 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) { @@ -265,12 +230,12 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project { this.renderSpecial(startBatch, projection); } - + // DO the middle batchs.. - for (var i=startBatchIndex+1; i < endBatchIndex; i++) + for (var i=startBatchIndex+1; i < endBatchIndex; i++) { renderable = this.batchs[i]; - + if(renderable instanceof PIXI.WebGLBatch) { this.batchs[i].render(); @@ -280,7 +245,7 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project this.renderSpecial(renderable, projection); } } - + // DO the last batch.. if(endBatch instanceof PIXI.WebGLBatch) { @@ -302,6 +267,8 @@ PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, project */ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) { + var sta = PIXI.shaderStack.length; + var worldVisible = renderable.vcount === PIXI.visibleCount if(renderable instanceof PIXI.TilingSprite) @@ -322,27 +289,58 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) } else if(renderable instanceof PIXI.FilterBlock) { - /* - * for now only masks are supported.. - */ + this.handleFilterBlock(renderable, projection); + } +} - var gl = PIXI.gl; +PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(renderable, projection) +{ + /* + * for now only masks are supported.. + */ + var gl = PIXI.gl; - if(renderable.open) + if(renderable.open) + { + if(renderable.data instanceof Array) + { + var filter = renderable.data[0]; + + if(!filter.shader) + { + var shader = new PIXI.PixiShader(); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shader = shader + } + + PIXI.activateShader(filter.shader); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } + else { gl.enable(gl.STENCIL_TEST); - + gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,1,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE); - - PIXI.WebGLGraphics.renderGraphics(renderable.mask, projection); - - // we know this is a render texture so enable alpha too.. + PIXI.WebGLGraphics.renderGraphics(renderable.data, projection); + gl.colorMask(true, true, true, true); gl.stencilFunc(gl.NOTEQUAL,0,0xff); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } + } + else + { + if(renderable.data instanceof Array) + { + PIXI.popShader(); + gl.uniform2f(PIXI.currentShader.projectionVector, projection.x, projection.y); + } else { gl.disable(gl.STENCIL_TEST); @@ -359,11 +357,11 @@ PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection) */ 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 @@ -375,7 +373,7 @@ PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) 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 @@ -388,7 +386,7 @@ PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject) nextRenderable = nextRenderable._iNext; if(nextRenderable.renderable && nextRenderable.__renderGroup)break; } - + this.insertObject(displayObject, previousRenderable, nextRenderable); } @@ -410,13 +408,13 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * It keeps going back until it finds a sprite or the stage */ var previousRenderable = start; - while(previousRenderable != this.root) + 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 @@ -424,7 +422,7 @@ PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end) * scene graph */ var previousRenderable2 = end; - while(previousRenderable2 != this.root) + while(previousRenderable2 != this.root.first) { previousRenderable2 = previousRenderable2._iPrev; if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break; @@ -456,20 +454,20 @@ PIXI.WebGLRenderGroup.prototype.removeFilterBlocks = function(start, end) 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 @@ -482,22 +480,22 @@ PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayOb nextRenderable = nextRenderable._iNext; if(nextRenderable.renderable && nextRenderable.__renderGroup)break; } - - // one the display object hits this. we can break the loop - + + // one the display object hits this. we can break the loop + var tempObject = displayObject.first; var testObject = displayObject.last._iNext; - do + do { tempObject.__renderGroup = this; - + if(tempObject.renderable) { - + this.insertObject(tempObject, previousRenderable, nextRenderable); previousRenderable = tempObject; } - + tempObject = tempObject._iNext; } while(tempObject != testObject) @@ -513,10 +511,10 @@ PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayOb PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject) { if(displayObject.__renderGroup != this)return; - + // var displayObject = displayObject.first; var lastObject = displayObject.last; - do + do { displayObject.__renderGroup = null; if(displayObject.renderable)this.removeObject(displayObject); @@ -539,16 +537,16 @@ PIXI.WebGLRenderGroup.prototype.insertObject = function(displayObject, previousO // 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; @@ -566,13 +564,13 @@ PIXI.WebGLRenderGroup.prototype.insertObject = function(displayObject, previousO // 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) { @@ -588,18 +586,18 @@ PIXI.WebGLRenderGroup.prototype.insertObject = function(displayObject, previousO // THERE IS A SPLIT IN THIS BATCH! // var splitBatch = previousBatch.split(nextSprite); // COOL! - // add it back into the array + // add it back into the array /* * OOPS! * seems the new sprite is in the middle of a batch - * lets split it.. + * 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; } } @@ -608,21 +606,21 @@ PIXI.WebGLRenderGroup.prototype.insertObject = function(displayObject, previousO 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 + if(previousBatch) // if this is invalid it means { var index = this.batchs.indexOf( previousBatch ); this.batchs.splice(index+1, 0, batch); @@ -631,16 +629,16 @@ PIXI.WebGLRenderGroup.prototype.insertObject = function(displayObject, previousO { 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) { @@ -651,14 +649,14 @@ PIXI.WebGLRenderGroup.prototype.insertObject = function(displayObject, previousO 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! } @@ -676,31 +674,31 @@ 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 + // 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 + // add it back into the array /* * OOPS! * seems the new sprite is in the middle of a batch - * lets split it.. + * lets split it.. */ var index = this.batchs.indexOf( previousBatch ); this.batchs.splice(index+1, 0, item, splitBatch); @@ -729,25 +727,25 @@ 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; @@ -757,15 +755,15 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) { batchToRemove = displayObject; } - + /* * Looks like there is somthing that needs removing! */ - if(batchToRemove) + 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) @@ -773,29 +771,30 @@ PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject) // 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 * @@ -808,26 +807,26 @@ 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); @@ -839,7 +838,7 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) 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) @@ -866,23 +865,19 @@ PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite) PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) { var gl = this.gl; - var shaderProgram = PIXI.shaderProgram; -// mat - //var mat4Real = PIXI.mat3.toMat4(strip.worldTransform); - //PIXI.mat4.transpose(mat4Real); - //PIXI.mat4.multiply(projectionMatrix, mat4Real, mat4Real ) - - - gl.useProgram(PIXI.stripShaderProgram); + var shaderProgram = PIXI.stripShaderProgram; + + + gl.useProgram(shaderProgram); var m = PIXI.mat3.clone(strip.worldTransform); - + PIXI.mat3.transpose(m); - - // set the matrix transform for the - gl.uniformMatrix3fv(PIXI.stripShaderProgram.translationMatrix, false, m); - gl.uniform2f(PIXI.stripShaderProgram.projectionVector, projection.x, projection.y); - gl.uniform1f(PIXI.stripShaderProgram.alpha, strip.worldAlpha); + + // set the matrix transform for the + gl.uniformMatrix3fv(shaderProgram.translationMatrix, false, m); + gl.uniform2f(shaderProgram.projectionVector, projection.x, projection.y); + gl.uniform1f(shaderProgram.alpha, strip.worldAlpha); /* if(strip.blendMode == PIXI.blendModes.NORMAL) @@ -894,25 +889,25 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR); } */ - - + + if(!strip.dirty) { - + gl.bindBuffer(gl.ARRAY_BUFFER, strip._vertexBuffer); gl.bufferSubData(gl.ARRAY_BUFFER, 0, strip.verticies) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); - + // update the uvs gl.bindBuffer(gl.ARRAY_BUFFER, strip._uvBuffer); gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, 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(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0); - + // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer); } @@ -922,29 +917,28 @@ PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection) gl.bindBuffer(gl.ARRAY_BUFFER, strip._vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, strip.verticies, gl.STATIC_DRAW) gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 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(shaderProgram.textureCoordAttribute, 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.bufferData(gl.ARRAY_BUFFER, strip.colors, gl.STATIC_DRAW) gl.vertexAttribPointer(shaderProgram.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); - + } - //console.log(gl.TRIANGLE_STRIP); - + gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0); - - gl.useProgram(PIXI.shaderProgram); + + gl.useProgram(PIXI.currentProgram); } /** @@ -959,31 +953,31 @@ PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projection { 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); } @@ -999,12 +993,12 @@ 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); @@ -1014,7 +1008,8 @@ PIXI.WebGLRenderGroup.prototype.initStrip = function(strip) 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 ad88e5b..84b5c89 100644 --- a/src/pixi/renderers/webgl/WebGLRenderer.js +++ b/src/pixi/renderers/webgl/WebGLRenderer.js @@ -21,7 +21,7 @@ PIXI.gl; * @param view {Canvas} the canvas to use as a view, optional * @param transparent=false {Boolean} the transparency of the render view, default false * @param antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) - * + * */ PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) { @@ -32,36 +32,37 @@ PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) this.width = width || 800; this.height = height || 600; - this.view = view || document.createElement( 'canvas' ); + this.view = view || document.createElement( 'canvas' ); this.view.width = this.width; this.view.height = this.height; - // deal with losing context.. + // 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) this.batchs = []; - try + try { - PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { + PIXI.gl = this.gl = this.view.getContext("experimental-webgl", { alpha: this.transparent, antialias:!!antialias, // SPEED UP?? premultipliedAlpha:false, stencil:true }); - } - catch (e) + } + catch (e) { throw new Error(" This browser does not support webGL. Try using the canvas renderer" + this); } - PIXI.initPrimitiveShader(); PIXI.initDefaultShader(); + PIXI.initPrimitiveShader(); PIXI.initDefaultStripShader(); - PIXI.activateDefaultShader(); + +// PIXI.activateDefaultShader(); var gl = this.gl; PIXI.WebGLRenderer.gl = gl; @@ -71,14 +72,17 @@ PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); - gl.colorMask(true, true, true, this.transparent); + gl.colorMask(true, true, true, this.transparent); PIXI.projection = new PIXI.Point(400, 300); this.resize(this.width, this.height); this.contextLost = false; + PIXI.activateShader(PIXI.defaultShader); + this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl); + } // constructor @@ -90,7 +94,7 @@ PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; * @static * @method getBatch * @return {WebGLBatch} - * @private + * @private */ PIXI.WebGLRenderer.getBatch = function() { @@ -114,7 +118,7 @@ PIXI.WebGLRenderer.getBatch = function() */ PIXI.WebGLRenderer.returnBatch = function(batch) { - batch.clean(); + batch.clean(); PIXI._batchs.push(batch); } @@ -127,8 +131,8 @@ PIXI.WebGLRenderer.returnBatch = function(batch) PIXI.WebGLRenderer.prototype.render = function(stage) { if(this.contextLost)return; - - + + // if rendering a new stage clear the batchs.. if(this.__stage !== stage) { @@ -137,8 +141,8 @@ PIXI.WebGLRenderer.prototype.render = function(stage) this.__stage = stage; this.stageRenderGroup.setRenderable(stage); } - - // TODO not needed now... + + // TODO not needed now... // update children if need be // best to remove first! /*for (var i=0; i < stage.__childrenRemoved.length; i++) @@ -147,29 +151,29 @@ PIXI.WebGLRenderer.prototype.render = function(stage) if(group)group.removeDisplayObject(stage.__childrenRemoved[i]); }*/ - // update any textures + // update any textures PIXI.WebGLRenderer.updateTextures(); - - // update the scene graph + + // 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.viewport(0, 0, this.width, this.height); - + gl.colorMask(true, true, true, this.transparent); + gl.viewport(0, 0, this.width, this.height); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); + + gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); gl.clear(gl.COLOR_BUFFER_BIT); // HACK TO TEST - + this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit; this.stageRenderGroup.render(PIXI.projection); - + // interaction // run interaction! if(stage.interactive) @@ -181,15 +185,15 @@ 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) { - for (var i=0; i < PIXI.Texture.frameUpdates.length; i++) + for (var i=0; i < PIXI.Texture.frameUpdates.length; i++) { PIXI.Texture.frameUpdates[i].updateFrame = false; }; - + PIXI.Texture.frameUpdates = []; } } @@ -222,7 +226,7 @@ PIXI.WebGLRenderer.updateTexture = function(texture) { //TODO break this out into a texture manager... var gl = PIXI.gl; - + if(!texture._glTexture) { texture._glTexture = gl.createTexture(); @@ -288,7 +292,7 @@ PIXI.WebGLRenderer.prototype.resize = function(width, height) this.view.width = width; this.view.height = height; - this.gl.viewport(0, 0, this.width, this.height); + this.gl.viewport(0, 0, this.width, this.height); //var projectionMatrix = this.projectionMatrix; @@ -323,20 +327,20 @@ PIXI.WebGLRenderer.prototype.handleContextLost = function(event) */ PIXI.WebGLRenderer.prototype.handleContextRestored = function(event) { - this.gl = this.view.getContext("experimental-webgl", { + this.gl = this.view.getContext("experimental-webgl", { alpha: true }); - this.initShaders(); + this.initShaders(); - for(var key in PIXI.TextureCache) + for(var key in PIXI.TextureCache) { var texture = PIXI.TextureCache[key].baseTexture; texture._glTexture = null; PIXI.WebGLRenderer.updateTexture(texture); }; - for (var i=0; i < this.batchs.length; i++) + for (var i=0; i < this.batchs.length; i++) { this.batchs[i].restoreLostContext(this.gl)// this.batchs[i].dirty = true; diff --git a/src/pixi/renderers/webgl/WebGLShaders.js b/src/pixi/renderers/webgl/WebGLShaders.js index 93cf578..4f3724d 100644 --- a/src/pixi/renderers/webgl/WebGLShaders.js +++ b/src/pixi/renderers/webgl/WebGLShaders.js @@ -1,4 +1,3 @@ - /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ @@ -23,13 +22,11 @@ PIXI.shaderVertexSrc = [ "attribute vec2 aVertexPosition;", "attribute vec2 aTextureCoord;", "attribute float aColor;", - //"uniform mat4 uMVMatrix;", - + "uniform vec2 projectionVector;", "varying vec2 vTextureCoord;", "varying float vColor;", "void main(void) {", - // "gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0, 1.0);", "gl_Position = vec4( aVertexPosition.x / projectionVector.x -1.0, aVertexPosition.y / -projectionVector.y + 1.0 , 0.0, 1.0);", "vTextureCoord = aTextureCoord;", "vColor = aColor;", @@ -69,7 +66,6 @@ PIXI.stripShaderVertexSrc = [ "}" ]; - /* * primitive shader.. */ @@ -96,48 +92,49 @@ PIXI.primitiveShaderVertexSrc = [ "}" ]; -PIXI.initPrimitiveShader = function() +PIXI.shaderStack = []; + +PIXI.initPrimitiveShader = function() { var gl = PIXI.gl; var shaderProgram = PIXI.compileProgram(PIXI.primitiveShaderVertexSrc, PIXI.primitiveShaderFragmentSrc) - + gl.useProgram(shaderProgram); shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - + shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); shaderProgram.translationMatrix = gl.getUniformLocation(shaderProgram, "translationMatrix"); - + + + //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(shaderProgram.colorAttribute); +//gl.enableVertexAttribArray(program.textureCoordAttribute); + shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); PIXI.primitiveProgram = shaderProgram; + + } -PIXI.initDefaultShader = function() +PIXI.initDefaultShader = function() { - var gl = this.gl; - var shaderProgram = PIXI.compileProgram(PIXI.shaderVertexSrc, PIXI.shaderFragmentSrc) - - gl.useProgram(shaderProgram); - - shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord"); - shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - - // shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - - PIXI.shaderProgram = shaderProgram; + PIXI.defaultShader = new PIXI.PixiShader(); + PIXI.defaultShader.init(); + PIXI.activateShader(PIXI.defaultShader); + /* + PIXI.shaderStack.push(PIXI.defaultShader); + PIXI.current*/ } -PIXI.initDefaultStripShader = function() +PIXI.initDefaultStripShader = function() { var gl = this.gl; var shaderProgram = PIXI.compileProgram(PIXI.stripShaderVertexSrc, PIXI.stripShaderFragmentSrc) - + gl.useProgram(shaderProgram); shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); @@ -147,11 +144,9 @@ PIXI.initDefaultStripShader = function() shaderProgram.alpha = gl.getUniformLocation(shaderProgram, "alpha"); shaderProgram.colorAttribute = gl.getAttribLocation(shaderProgram, "aColor"); - shaderProgram.projectionVector = gl.getUniformLocation(shaderProgram, "projectionVector"); - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - + PIXI.stripShaderProgram = shaderProgram; } @@ -186,9 +181,9 @@ 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); @@ -198,34 +193,64 @@ PIXI.compileProgram = function(vertexSrc, fragmentSrc) } return shaderProgram; +} + +PIXI.activateShader = function(shader) +{ + PIXI.shaderStack.push(shader); + + //console.log(">>>") + var gl = PIXI.gl; + + var shaderProgram = shader.program; + + // map uniforms.. + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.enableVertexAttribArray(shaderProgram.colorAttribute); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + + shader.syncUniforms(); + + PIXI.currentShader = shaderProgram; } -PIXI.activateDefaultShader = function() +PIXI.popShader = function() { var gl = PIXI.gl; - var shaderProgram = PIXI.shaderProgram; - + // activate last program.. + var lastProgram = PIXI.shaderStack.pop(); + + var shaderProgram = PIXI.shaderStack[ PIXI.shaderStack.length-1 ].program; + gl.useProgram(shaderProgram); - - - gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); - gl.enableVertexAttribArray(shaderProgram.colorAttribute); + + PIXI.currentShader = shaderProgram; } - - PIXI.activatePrimitiveShader = function() { var gl = PIXI.gl; - - gl.disableVertexAttribArray(PIXI.shaderProgram.textureCoordAttribute); - gl.disableVertexAttribArray(PIXI.shaderProgram.colorAttribute); - + gl.useProgram(PIXI.primitiveProgram); + + //gl.disableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.disableVertexAttribArray(PIXI.currentShader.colorAttribute); + gl.disableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + + //gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); +} - gl.enableVertexAttribArray(PIXI.primitiveProgram.vertexPositionAttribute); - gl.enableVertexAttribArray(PIXI.primitiveProgram.colorAttribute); -} +PIXI.deactivatePrimitiveShader = function() +{ + var gl = PIXI.gl; + gl.useProgram(PIXI.currentShader); + + gl.enableVertexAttribArray(PIXI.currentShader.textureCoordAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.vertexPositionAttribute); + //gl.enableVertexAttribArray(PIXI.currentShader.colorAttribute); +} \ No newline at end of file diff --git a/src/pixi/textures/RenderTexture.js b/src/pixi/textures/RenderTexture.js index d584420..738c1d7 100644 --- a/src/pixi/textures/RenderTexture.js +++ b/src/pixi/textures/RenderTexture.js @@ -5,11 +5,11 @@ /** 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. - Otherwise black rectangles will be drawn instead. - + __Hint__: All DisplayObjects (exmpl. Sprites) that renders 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: - + var renderTexture = new PIXI.RenderTexture(800, 600); var sprite = PIXI.Sprite.fromImage("spinObj_01.png"); sprite.position.x = 800/2; @@ -37,9 +37,9 @@ PIXI.RenderTexture = function(width, height) this.width = width || 100; this.height = height || 100; - this.identityMatrix = PIXI.mat3.create(); + this.indetityMatrix = PIXI.mat3.create(); - this.frame = new PIXI.Rectangle(0, 0, this.width, this.height); + this.frame = new PIXI.Rectangle(0, 0, this.width, this.height); if(PIXI.gl) { @@ -68,7 +68,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer ); this.glFramebuffer.width = this.width; - this.glFramebuffer.height = this.height; + this.glFramebuffer.height = this.height; this.baseTexture = new PIXI.BaseTexture(); @@ -96,7 +96,7 @@ PIXI.RenderTexture.prototype.initWebGL = function() // set the correct render function.. this.render = this.renderWebGL; - + } @@ -105,19 +105,19 @@ PIXI.RenderTexture.prototype.resize = function(width, height) this.width = width; this.height = height; - + if(PIXI.gl) { this.projection.x = this.width/2 this.projection.y = this.height/2; - + var gl = PIXI.gl; 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); } else { - + this.frame.width = this.width this.frame.height = this.height; this.renderer.resize(this.width, this.height); @@ -153,15 +153,15 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle var gl = PIXI.gl; // enable the alpha color mask.. - gl.colorMask(true, true, true, true); + gl.colorMask(true, true, true, true); - gl.viewport(0, 0, this.width, this.height); + gl.viewport(0, 0, this.width, this.height); gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer ); if(clear) { - gl.clearColor(0,0,0, 0); + gl.clearColor(0,0,0, 0); gl.clear(gl.COLOR_BUFFER_BIT); } @@ -170,24 +170,24 @@ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, cle //TODO -? create a new one??? dont think so! var originalWorldTransform = displayObject.worldTransform; - displayObject.worldTransform = PIXI.mat3.create();//sthis.identityMatrix; + displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; // modify to flip... displayObject.worldTransform[4] = -1; displayObject.worldTransform[5] = this.projection.y * 2; - + if(position) { displayObject.worldTransform[2] = position.x; displayObject.worldTransform[5] -= position.y; } - + PIXI.visibleCount++; displayObject.vcount = PIXI.visibleCount; - + for(var i=0,j=children.length; i